├── rust ├── .gitignore ├── README.md ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── c ├── golf │ ├── .gitignore │ └── Makefile ├── vm │ ├── .gitignore │ ├── src │ │ ├── knight.h │ │ ├── vm.h │ │ ├── env.h │ │ ├── knight.c │ │ ├── main.c │ │ ├── string.h │ │ ├── shared.c │ │ ├── shared.h │ │ ├── string.c │ │ ├── value.h │ │ ├── bytecode.h │ │ └── env.c │ └── Makefile ├── c-old │ ├── .gitignore │ ├── src │ │ ├── shared.c │ │ ├── knight.c │ │ ├── knight.h │ │ ├── env.h │ │ ├── shared.h │ │ ├── string.c │ │ ├── main.c │ │ ├── string.h │ │ └── ast.h │ └── Makefile ├── ast │ ├── .gitignore │ ├── src │ │ ├── parse.h │ │ ├── ast.c │ │ ├── knight.c │ │ ├── knight.h │ │ ├── ast.h │ │ ├── shared.c │ │ ├── shared.h │ │ ├── env.h │ │ └── main.c │ ├── Makefile │ └── README.md └── ast-ext │ ├── .gitignore │ ├── src │ ├── parse.h │ ├── ast.c │ ├── knight.c │ ├── knight.h │ ├── shared.c │ ├── shared.h │ ├── ast.h │ ├── main.c │ ├── env.h │ └── custom.h │ ├── Makefile │ ├── ext │ ├── list.h │ └── helloworld.c │ └── README.md ├── raku ├── .gitignore ├── lib │ ├── Knight.rakumod │ └── Knight │ │ ├── Boolean.rakumod │ │ ├── NonIdempotent.rakumod │ │ ├── String.rakumod │ │ ├── Null.rakumod │ │ ├── Identifier.rakumod │ │ ├── TypedValue.rakumod │ │ ├── Number.rakumod │ │ └── Value.rakumod └── main.raku ├── cpp ├── .gitignore ├── src │ ├── error.cpp │ ├── knight.cpp │ ├── error.hpp │ ├── knight.hpp │ ├── main.cpp │ ├── variable.hpp │ ├── variable.cpp │ ├── value.hpp │ └── function.hpp └── Makefile ├── python ├── .gitignore ├── knight │ ├── error.py │ ├── __init__.py │ ├── boolean.py │ ├── value.py │ ├── stream.py │ ├── literal.py │ ├── null.py │ ├── identifier.py │ ├── string.py │ └── number.py └── main.py ├── csharp ├── .gitignore ├── IValue.cs ├── Knight.csproj ├── NonIdempotent.cs ├── Ops.cs ├── Literal.cs ├── Null.cs ├── Error.cs ├── Boolean.cs ├── Identifier.cs ├── Stream.cs ├── Number.cs ├── String.cs └── Knight.cs ├── examples ├── helloworld.kn ├── fib-min.kn ├── fact.kn ├── fizzbuzz.kn ├── gussing.kn └── fib.kn ├── asm ├── tmp ├── knight ├── old │ ├── value_header.s │ ├── env.s │ ├── debug.s │ └── string.s ├── value_header.s ├── tmp.c ├── env.s ├── debug.s └── string.s ├── test ├── tests │ ├── integration.rb │ ├── shared.rb │ ├── null.rb │ ├── variable.rb │ └── block.rb ├── runtest └── main.rb ├── quest └── main.qs ├── ruby ├── error.rb ├── null.rb ├── string.rb ├── boolean.rb ├── identifier.rb ├── number.rb ├── knight │ └── lib │ │ └── boolean2.rb ├── knight.rb ├── golf.rb └── function.rb ├── squire ├── perl │ ├── lib │ │ ├── Parser.pm │ │ ├── Types │ │ │ ├── Null.pm │ │ │ ├── Boolean.pm │ │ │ ├── String.pm │ │ │ ├── Value.pm │ │ │ └── Number.pm │ │ └── Lexer.pm │ └── main.pl ├── README.md └── raku │ └── main.raku ├── haskell ├── src │ ├── Knight │ │ ├── tmp.hs │ │ └── Types.hs │ ├── Evaluator.hs │ └── Parser.hs ├── stack.yaml.lock ├── knight.cabal ├── LICENSE └── stack.yaml ├── perl ├── lib │ ├── Kn.pm │ └── Kn │ │ ├── Environment.pm │ │ ├── Boolean.pm │ │ ├── Identifier.pm │ │ ├── Number.pm │ │ ├── Null.pm │ │ ├── String.pm │ │ ├── Ast.pm │ │ └── Value.pm ├── README.md └── bin │ └── knight.pl ├── timeit ├── php ├── bin │ └── knight.php └── src │ ├── Knight.php │ ├── Nil.php │ ├── Stream.php │ ├── Boolean.php │ └── Value.php ├── shell └── knight.sed ├── javascript ├── src │ ├── error.js │ ├── knight.js │ ├── literal.js │ ├── bool.js │ ├── null.js │ ├── stream.js │ ├── ident.js │ └── value.js ├── bin │ └── knight.js └── package.json ├── COMMUNITY.md └── LICENSE /rust/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /c/golf/.gitignore: -------------------------------------------------------------------------------- 1 | knight 2 | *.o -------------------------------------------------------------------------------- /raku/.gitignore: -------------------------------------------------------------------------------- 1 | **/.precomp 2 | -------------------------------------------------------------------------------- /c/vm/.gitignore: -------------------------------------------------------------------------------- 1 | obj/* 2 | knight 3 | -------------------------------------------------------------------------------- /cpp/.gitignore: -------------------------------------------------------------------------------- 1 | obj/* 2 | knight 3 | -------------------------------------------------------------------------------- /python/.gitignore: -------------------------------------------------------------------------------- 1 | **/__pycache__/* 2 | -------------------------------------------------------------------------------- /c/c-old/.gitignore: -------------------------------------------------------------------------------- 1 | obj/* 2 | knight 3 | -------------------------------------------------------------------------------- /csharp/.gitignore: -------------------------------------------------------------------------------- 1 | obj 2 | bin 3 | .vscode -------------------------------------------------------------------------------- /c/ast/.gitignore: -------------------------------------------------------------------------------- 1 | obj/* 2 | knight 3 | knight.dSYM -------------------------------------------------------------------------------- /c/ast-ext/.gitignore: -------------------------------------------------------------------------------- 1 | obj/* 2 | knight 3 | knight.dSYM -------------------------------------------------------------------------------- /examples/helloworld.kn: -------------------------------------------------------------------------------- 1 | : OUTPUT + "Hello," " world!" 2 | -------------------------------------------------------------------------------- /asm/tmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sampersand/knight/HEAD/asm/tmp -------------------------------------------------------------------------------- /asm/knight: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sampersand/knight/HEAD/asm/knight -------------------------------------------------------------------------------- /test/tests/integration.rb: -------------------------------------------------------------------------------- 1 | # eg fizzbuzz, knight in knight, recursion, etc 2 | -------------------------------------------------------------------------------- /quest/main.qs: -------------------------------------------------------------------------------- 1 | text = Io::File('../examples/fizzbuzz.kn').read(); 2 | 3 | print(text); 4 | -------------------------------------------------------------------------------- /examples/fib-min.kn: -------------------------------------------------------------------------------- 1 | ;=fB I Int 5 | sumOdd x = sum $ sum <$> filter (odd . length) (filter (flip isInfixOf x) (subsequences x)) 6 | 7 | main = print (sumOdd [1,4,2,5,3]) 8 | -------------------------------------------------------------------------------- /perl/lib/Kn.pm: -------------------------------------------------------------------------------- 1 | package Kn; 2 | use strict; 3 | use warnings; 4 | 5 | use Kn::Value; 6 | 7 | # Runs the given argument as a knight program. 8 | sub run { 9 | my $str = "$_[1]"; 10 | Kn::Value->parse(\$str)->run(); 11 | } 12 | 13 | 1; 14 | -------------------------------------------------------------------------------- /squire/README.md: -------------------------------------------------------------------------------- 1 | I originally wanted to have "Knight" and "Squire" languages---the Knight would be simpler, and Squire would be more complex. However, I decided against it, as I like the purity of just Knight. But, I left this here for posterity. 2 | -------------------------------------------------------------------------------- /python/knight/error.py: -------------------------------------------------------------------------------- 1 | class Error(Exception): 2 | """ The parent error class to all Knight errors.""" 3 | 4 | class ParseError(Error): 5 | """ A problem occurred whilst parsing Knight code. """ 6 | 7 | class RunError(Error): 8 | """ A problem occurred whilst running Knight code. """ 9 | -------------------------------------------------------------------------------- /examples/fact.kn: -------------------------------------------------------------------------------- 1 | ;=fB*iI>1=i-i 1 1Cf;=i 10O Cf 2 | ;=fB*iI<=i-i 1 2iCf;=i 10O Cf 3 | 4 | ; = f B ; O i * i (I<=i-i 2 i 1Cf) ; = i 10 : O C f 5 | 6 | 7 | ; = f B : * i (I<=i-i 2 i 1Cf) ; = i 10 : O C f 8 | zsh: segmentation fault ./main -e '; = f B : * i (I<=i-i 2 i 1Cf) ; = i 10 : O C f' 9 | -------------------------------------------------------------------------------- /examples/fizzbuzz.kn: -------------------------------------------------------------------------------- 1 | ; = fizzbuzz BLOCK 2 | ; = n 0 3 | ; = max (+ 1 max) 4 | : WHILE < (= n + 1 n) max 5 | : OUTPUT 6 | : IF ! (% n 15) "FizzBuzz" 7 | : IF ! (% n 5) "Fizz" 8 | : IF ! (% n 3) "Buzz" 9 | n 10 | ; = max 100 11 | : CALL fizzbuzz 12 | 13 | -------------------------------------------------------------------------------- /ruby/null.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Kn 3 | class Null 4 | NULL = new.freeze 5 | 6 | def self.new = NULL 7 | 8 | def inspect = 'Null()' 9 | 10 | def to_i = 0 11 | def to_s = 'null' 12 | def truthy? = false 13 | 14 | alias == equal? 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /c/vm/src/env.h: -------------------------------------------------------------------------------- 1 | #ifndef ENV_H 2 | #define ENV_H 3 | 4 | #include "value.h" 5 | 6 | typedef struct _variable_t { 7 | value_t value; 8 | const char *name; 9 | } variable_t; 10 | 11 | void env_init(size_t); 12 | void env_free(void); 13 | variable_t *env_fetch(const char *, bool); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /csharp/Knight.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 8 6 | Exe 7 | Knight 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /squire/perl/lib/Types/Null.pm: -------------------------------------------------------------------------------- 1 | package Null; 2 | 3 | use Value; 4 | use base qw(Value); 5 | 6 | use strict; 7 | use warnings; 8 | 9 | sub new { 10 | my $class = shift; 11 | bless {}, $class; 12 | } 13 | 14 | sub to_string { 15 | String->new("null"); 16 | } 17 | 18 | sub to_bool { 19 | Boolean->new(0); 20 | } 21 | 22 | 1; 23 | -------------------------------------------------------------------------------- /squire/raku/main.raku: -------------------------------------------------------------------------------- 1 | sub run_ast(@ast) { 2 | given @ast[0] { 3 | when Number, Array, String, Boolean, Null { @ast[0] } 4 | when "+" { run_ast(@ast[1]) + run_ast(@ast[2]) } 5 | when "-" { run_ast(@ast[1]) - run_ast(@ast[2]) } 6 | when "*" { run_ast(@ast[1]) * run_ast(@ast[3]) } 7 | ... 8 | } 9 | } 10 | sub op_add($a, $b) { $a + $b } 11 | -------------------------------------------------------------------------------- /cpp/src/error.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace kn { 8 | // the base class for all errors within Knight. 9 | struct Error : public std::runtime_error { 10 | // Creates a new error with the given message. 11 | explicit Error(std::string const& what_arg); 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /csharp/NonIdempotent.cs: -------------------------------------------------------------------------------- 1 | namespace Knight 2 | { 3 | public abstract class NonIdempotent : IValue 4 | { 5 | public abstract IValue Run(); 6 | public abstract void Dump(); 7 | 8 | public override string ToString() => Run().ToString(); 9 | public long ToNumber() => Run().ToNumber(); 10 | public bool ToBoolean() => Run().ToBoolean(); 11 | } 12 | } -------------------------------------------------------------------------------- /timeit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $# = 0 ]; then 4 | echo "usage: $0 [executable] [executable arguments...]" >/dev/stderr 5 | exit 1 6 | fi 7 | 8 | : "${KNIGHT_HOME:=$PWD}" 9 | 10 | for _ in $(seq 1 5); do 11 | time "$@" -f "$KNIGHT_HOME/knight.kn" </dev/null || exit 1 12 | $KNIGHT_HOME/knight.kn 13 | $KNIGHT_HOME/examples/fizzbuzz.kn 14 | EOS 15 | done -------------------------------------------------------------------------------- /c/golf/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | override CFLAGS+=-Wall -Wpedantic -Wextra -Werror 3 | ifdef DEBUG 4 | override CFLAGS+=-g -fsanitize=address,undefined 5 | else 6 | CFLAGS=-O1 7 | endif 8 | 9 | .PHONY: clean 10 | 11 | knight: knight.o 12 | $(CC) $(CFLAGS) knight.o -o knight 13 | 14 | %.o: %.c 15 | $(CC) $(CFLAGS) -c $< 16 | 17 | clean: 18 | -@rm *.o 19 | -@rm knight -------------------------------------------------------------------------------- /perl/README.md: -------------------------------------------------------------------------------- 1 | # Knight: Perl Edition 2 | 3 | I originally thought it'd be fun to have some fancy overloading nonsense for everything, but since that breaks the spirit of some of the operators (eg `<` is intended for numerical comparisons, `.` is for string concatenation, etc), I decided to opt for simply overloading the conversion operators: `""`, `0+`, and `bool`. 4 | 5 | -------------------------------------------------------------------------------- /php/bin/knight.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | " 6 | 7 | require_relative 'tests/number' 8 | require_relative 'tests/null' 9 | require_relative 'tests/boolean' 10 | require_relative 'tests/string' 11 | require_relative 'tests/block' 12 | require_relative 'tests/variable' 13 | require_relative 'tests/function' 14 | -------------------------------------------------------------------------------- /examples/gussing.kn: -------------------------------------------------------------------------------- 1 | ; OUTPUT "Maximum number? \" 2 | ; = max + 0 PROMPT 3 | ; = secret + 1 (% RANDOM max) 4 | ; = guess (= guesses 0) 5 | ; WHILE (! ? guess secret) { 6 | ; OUTPUT ++ "Guess a number from 1-" max "\" 7 | ; = guess + 0 PROMPT 8 | ; = guesses + guesses 1 9 | : OUTPUT ( 10 | IF (< guess secret) "too low!" 11 | IF (> guess secret) "too high!" "perfect!") 12 | } 13 | : OUTPUT ++ "It took you " guesses " guesses" 14 | -------------------------------------------------------------------------------- /c/vm/src/knight.c: -------------------------------------------------------------------------------- 1 | #include "knight.h" 2 | #include "bytecode.h" 3 | #include "vm.h" 4 | #include "env.h" 5 | #include 6 | #include 7 | 8 | void initialize() { 9 | env_init(2048); 10 | srand(time(NULL)); 11 | } 12 | 13 | value_t execute(const char *string) { 14 | block_t *block = block_parse(&string); 15 | 16 | block_dump(block); 17 | 18 | return vm_run(block); 19 | // return vm_run(block_parse(&string)); 20 | } 21 | -------------------------------------------------------------------------------- /test/main.rb: -------------------------------------------------------------------------------- 1 | # working 2 | $executable_to_test = '../perl/knight' 3 | $executable_to_test = '../php/bin/knight' 4 | $executable_to_test = '../raku/main.raku' 5 | 6 | # in proress 7 | $executable_to_test = ['../shell/knight.awk', '--'] 8 | $executable_to_test = '../shell/knight.sh' 9 | $executable_to_test = '../c++/knight' 10 | $executable_to_test = '../c/knight' 11 | $executable_to_test = '../raku/main.raku' 12 | require_relative 'runtest' 13 | -------------------------------------------------------------------------------- /raku/main.raku: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env raku 2 | 3 | use lib $*PROGRAM.parent.add('lib'); 4 | use Knight; 5 | 6 | @*ARGS = @*ARGS.join('=') if @*ARGS.elems == 2; 7 | 8 | multi sub MAIN(Str :$) is hidden-from-USAGE { 9 | say $*USAGE; 10 | } 11 | 12 | #| the expression to execute 13 | multi sub MAIN(Str :e(:$expr)) { 14 | Knight::run $expr; 15 | } 16 | 17 | #| the file to execute 18 | multi sub MAIN(Str :f(:$file)) { 19 | MAIN expr => $file.IO.slurp 20 | } 21 | -------------------------------------------------------------------------------- /python/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import knight 4 | from sys import setrecursionlimit, argv 5 | 6 | # This is needed to run FizzBuzz in Knight in Python. 7 | setrecursionlimit(100000) 8 | 9 | try: 10 | _, flag, program = argv 11 | assert flag in ['-e', '-f'] 12 | except: 13 | quit(f"usage: {argv[0]} (-e 'program' | -f file)") 14 | 15 | if flag == '-f': 16 | with open(program) as f: 17 | program = f.read() 18 | 19 | knight.run(program) 20 | -------------------------------------------------------------------------------- /raku/lib/Knight/Boolean.rakumod: -------------------------------------------------------------------------------- 1 | use Knight::TypedValue; 2 | 3 | #| The Boolean class within Knight. 4 | unit class Knight::Boolean does Knight::TypedValue[Bool, * <=> *, * == *]; 5 | 6 | #| Returns either `true` or `false`, depending on whether we are empty. 7 | method Str(--> Str) is pure { 8 | $!value ?? 'true' !! 'false' 9 | } 10 | 11 | #| Gets an internal representation of the class; used in debugging. 12 | method gist(--> Str) { 13 | "Boolean($.Str)"; 14 | } 15 | -------------------------------------------------------------------------------- /shell/knight.sed: -------------------------------------------------------------------------------- 1 | #n 2 | 3 | # this program isn't done at all, :( 4 | 5 | s/^$/foobar/;h;s/.*/baz/;H 6 | 7 | 8 | s/.*/1299/ 9 | s/.*/299/ 10 | s/.*/300/ 11 | 12 | :inc 13 | q 14 | # add a leading `0` if we need a new digit 15 | s/([^0-9]|$)(9+)$/\10\2/ 16 | /[0-8]9*$/ { 17 | s//&/ 18 | G 19 | h 20 | s/^[^]*.([0-9]+)\n.*/\1/ 21 | y/0123456789/1234567890/ 22 | G 23 | s/^([0-9]+)\n([^]*).[0-9]+(.*)/\2\1\3/ 24 | h 25 | s/[^]*.\n// 26 | x 27 | s/\n.*// 28 | } 29 | l 30 | -------------------------------------------------------------------------------- /javascript/src/error.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The parent class for all errors that can arise within Knight. 3 | */ 4 | export class KnightError extends Error { } 5 | 6 | /** 7 | * The class that represents errors that can occur during the parsing of a 8 | * Knight program. 9 | */ 10 | export class ParseError extends KnightError { } 11 | 12 | /** 13 | * The class that represents errors that can occur during the execution of a 14 | * Knight program. 15 | */ 16 | export class RuntimeError extends KnightError { } 17 | -------------------------------------------------------------------------------- /perl/lib/Kn/Boolean.pm: -------------------------------------------------------------------------------- 1 | package Kn::Boolean; 2 | use strict; 3 | use warnings; 4 | 5 | use parent 'Kn::Value'; 6 | 7 | use overload 8 | '""' => sub { shift() ? 'true' : 'false'; }; 9 | 10 | # Parses a new boolean. 11 | sub parse { 12 | my ($class, $stream) = @_; 13 | 14 | $$stream =~ s/\A([TF])[A-Z]*//p or return; 15 | 16 | $class->new($1 eq 'T'); 17 | } 18 | 19 | # Dumps the class's info. Used for debugging. 20 | sub dump { 21 | 'Boolean(' . (shift() ? 'true' : 'false') . ')'; 22 | } 23 | 24 | 1; 25 | -------------------------------------------------------------------------------- /ruby/string.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Kn 4 | class String 5 | include Comparable 6 | 7 | def initialize(str) = @str = str 8 | 9 | def inspect = "String(#@str)" 10 | 11 | def to_i = @str.to_i 12 | def to_s = @str 13 | def truthy? = !@str.empty? 14 | 15 | def +(rhs) = String.new(@str + rhs.to_s) 16 | def *(rhs) = String.new(@str * rhs.to_i) 17 | 18 | def <=>(rhs) = @str <=> rhs.to_s 19 | def ==(rhs) = rhs.is_a?(self.class) && @str == rhs.to_s 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /haskell/stack.yaml.lock: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by Stack. 2 | # You should not edit this file by hand. 3 | # For more information, please see the documentation at: 4 | # https://docs.haskellstack.org/en/stable/lock_files 5 | 6 | packages: [] 7 | snapshots: 8 | - completed: 9 | size: 532386 10 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/16/17.yaml 11 | sha256: d3ee1ae797cf63189c95cf27f00700304946c5cb3c1e6a82001cd6584a221e1b 12 | original: lts-16.17 13 | -------------------------------------------------------------------------------- /csharp/Ops.cs: -------------------------------------------------------------------------------- 1 | namespace Knight.Ops 2 | { 3 | public interface IAdd 4 | { 5 | IValue Add(IValue rhs); 6 | } 7 | 8 | public interface ISub 9 | { 10 | IValue Sub(IValue rhs); 11 | } 12 | 13 | public interface IMul 14 | { 15 | IValue Mul(IValue rhs); 16 | } 17 | 18 | public interface IDiv 19 | { 20 | IValue Div(IValue rhs); 21 | } 22 | 23 | public interface IMod 24 | { 25 | IValue Mod(IValue rhs); 26 | } 27 | 28 | public interface IPow 29 | { 30 | IValue Pow(IValue rhs); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /COMMUNITY.md: -------------------------------------------------------------------------------- 1 | # Community 2 | The Knight Community's constantly growing! We'd love to have you! 3 | 4 | ## Discord 5 | We have a discord! Join us: https://discord.gg/SE3TjsewDk 6 | 7 | ## Implementations 8 | The following is a list of Knight implementations, some of which are in progress. 9 | 10 | - [sampersand](https://github.com/sampersand): https://github.com/sampersand/knight 11 | - [wreien](https://github.com/wreien): https://github.com/wreien/knight 12 | - [zero9178]((https://github.com/zero9178): https://github.com/zero9178/knight_me -------------------------------------------------------------------------------- /cpp/src/knight.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "value.hpp" 4 | #include 5 | 6 | namespace kn { 7 | // Initializes the Knight interpreter. This must be run before all other types are. 8 | void initialize(); 9 | 10 | // Runs the input as Knight source code, returning its result. 11 | template 12 | Value run(T input) { 13 | std::string_view view(input); 14 | auto value = Value::parse(view); 15 | 16 | if (!value) 17 | throw Error("cannot parse a value"); 18 | 19 | return value->run(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /csharp/Literal.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Knight 4 | { 5 | public abstract class Literal : IValue, IEquatable 6 | where T: IEquatable 7 | { 8 | protected readonly T _data; 9 | 10 | public Literal(T data) => _data = data; 11 | public IValue Run() => this; 12 | public abstract void Dump(); 13 | 14 | public override string ToString() => _data.ToString(); 15 | public abstract bool ToBoolean(); 16 | public abstract long ToNumber(); 17 | 18 | public bool Equals(IValue obj) => GetType() == obj.GetType() && _data.Equals(((Literal) obj)._data); 19 | } 20 | } -------------------------------------------------------------------------------- /javascript/bin/knight.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { KnightError, run } from '../src/knight.js'; 4 | import { readFileSync } from 'fs'; 5 | 6 | const argv = process.argv; 7 | 8 | if (argv.length !== 4 || (argv[2] !== '-e' && argv[2] !== '-f')) { 9 | console.error(`usage: ${argv[1]} (-e 'program' | -f file)`); 10 | process.exit(1); 11 | } 12 | 13 | try { 14 | run(argv[2] == '-e' ? argv[3] : readFileSync(argv[3])); 15 | } catch (error) { 16 | if (error instanceof KnightError) { 17 | console.error("Fatal Error:", error.message); 18 | process.exit(1); 19 | } else { 20 | throw error; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "knight", 3 | "type": "module", 4 | "version": "1.0.0", 5 | "description": "The Knight programming langauge, in JavaScript", 6 | "main": "bin/knight.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/sampersand/knight.git" 13 | }, 14 | "author": "Sam W", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/sampersand/knight/issues" 18 | }, 19 | "homepage": "https://github.com/sampersand/knight#readme" 20 | } 21 | -------------------------------------------------------------------------------- /python/knight/__init__.py: -------------------------------------------------------------------------------- 1 | from knight.stream import Stream 2 | from knight.error import Error, RunError, ParseError 3 | from knight.value import Value 4 | 5 | from knight.literal import Literal 6 | from knight.null import Null 7 | from knight.boolean import Boolean 8 | from knight.number import Number 9 | from knight.string import String 10 | from knight.identifier import Identifier 11 | from knight.function import Function 12 | 13 | def run(stream: str) -> Value: 14 | value = Value.parse(Stream(stream)) 15 | 16 | if value is None: 17 | raise ParseError('Nothing to parse.') 18 | else: 19 | return value.run() 20 | 21 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "knightrs" 3 | version = "0.8.8" 4 | authors = ["Sam Westerman "] 5 | edition = "2018" 6 | description = "The Knight programming language, in Rust" 7 | repository = "https://github.com/sampersand/knight/rust" 8 | license = "MIT" 9 | 10 | # keywords = [] 11 | # categories = [] 12 | 13 | [features] 14 | default = [] 15 | embedded = [] # remove functions that need to access the file system. 16 | # multithreaded = [] 17 | checked-overflow = [] 18 | 19 | [dependencies] 20 | rand = "0.8" 21 | once_cell = "1.7" 22 | lazy_static = "1.4" 23 | parking_lot = "0.11" 24 | clap = "2.33" 25 | -------------------------------------------------------------------------------- /csharp/Null.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Knight.Ops; 3 | 4 | namespace Knight 5 | { 6 | public class Null : IValue, IEquatable 7 | { 8 | public static Null Parse(Stream stream) { 9 | if (!stream.StartsWith('N')) { 10 | return null; 11 | } 12 | 13 | stream.StripKeyword(); 14 | 15 | return new Null(); 16 | } 17 | 18 | public IValue Run() => this; 19 | public void Dump() => Console.Write("Null()"); 20 | public override string ToString() => "null"; 21 | public bool ToBoolean() => false; 22 | public long ToNumber() => 0; 23 | 24 | public bool Equals(IValue obj) => GetType() == obj.GetType(); 25 | 26 | } 27 | } -------------------------------------------------------------------------------- /squire/perl/lib/Lexer.pm: -------------------------------------------------------------------------------- 1 | package Lexer; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | # use overload 'bool' => sub { shift->{stream} }; 7 | # use overload 'bool' => sub { shift }, '""' => sub { my $x = shift; $$x }; 8 | 9 | sub new { 10 | my $class = shift; 11 | # bless { stream => shift }, $class; 12 | bless \shift, $class; 13 | } 14 | 15 | sub peek { 16 | substr shift->{stream}, 0, 1 17 | } 18 | 19 | sub advance { 20 | shift->{stream} =~ s/\A.//; 21 | } 22 | 23 | sub next { 24 | my $self = shift; 25 | 26 | # while $self->{stream} =~ s/\A\s+// or 27 | # return unless $self; 28 | # goto start if ; 29 | 1 30 | } 31 | 32 | 1; 33 | -------------------------------------------------------------------------------- /asm/old/value_header.s: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | abuse the fact that `malloc` will allocate things that are 16-aligned. 4 | 0...00000 = false 5 | 0...XXX10 = 62-bit integer 6 | 0...00100 = null 7 | 0...01100 = true 8 | X...X0001 = ident 9 | X...X0101 = function 10 | X...X1001 = string 11 | */ 12 | 13 | .equ FALSE_BITS, 0b0000 14 | .equ NUM_BIT, 0b0010 15 | .equ NULL_BITS, 0b0100 16 | .equ TRUE_BITS, 0b1100 17 | 18 | .equ ALLOC_BIT, 0b0001 19 | .equ TAG_MASK, 0b1111 20 | 21 | .equ FUNC_BIT, 0b0100 22 | .equ FUNC_TAG, FUNC_BIT | ALLOC_BIT 23 | .equ STRING_BIT, 0b1000 24 | .equ STRING_TAG, STRING_BIT | ALLOC_BIT 25 | .equ IDENT_TAG, 0b0000 | ALLOC_BIT 26 | 27 | -------------------------------------------------------------------------------- /asm/value_header.s: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | abuse the fact that `malloc` will allocate things that are 16-aligned. 4 | 0...00000 = false 5 | 0...XXX10 = 62-bit integer 6 | 0...00100 = null 7 | 0...01100 = true 8 | X...X0001 = ident 9 | X...X0101 = function 10 | X...X1001 = string 11 | */ 12 | 13 | .equ FALSE_BITS, 0b0000 14 | .equ NUM_BIT, 0b0010 15 | .equ NULL_BITS, 0b0100 16 | .equ TRUE_BITS, 0b1100 17 | 18 | .equ ALLOC_BIT, 0b0001 19 | .equ TAG_MASK, 0b1111 20 | 21 | .equ FUNC_BIT, 0b0100 22 | .equ FUNC_TAG, FUNC_BIT | ALLOC_BIT 23 | .equ STRING_BIT, 0b1000 24 | .equ STRING_TAG, STRING_BIT | ALLOC_BIT 25 | .equ IDENT_TAG, 0b0000 | ALLOC_BIT 26 | 27 | -------------------------------------------------------------------------------- /squire/perl/lib/Types/Boolean.pm: -------------------------------------------------------------------------------- 1 | package Boolean; 2 | 3 | use Value; 4 | use base qw(Value); 5 | 6 | use strict; 7 | use warnings; 8 | 9 | sub not { Boolean->new(!shift->{value}); } 10 | 11 | sub band(){ Boolean->new(shift->{value} & shift->to_number()->{value}); } 12 | sub bor() { Boolean->new(shift->{value} | shift->to_number()->{value}); } 13 | sub bxor(){ Boolean->new(shift->{value} ^ shift->to_number()->{value}); } 14 | 15 | sub to_string { String->new(shift->{value} ? 'true' : 'false'); } 16 | sub to_boolean{ shift } 17 | sub to_number { Number->new(shift->{value} ? 1 : 0); } 18 | 19 | sub cmp() { shift->to_number()->cmp(shift); } 20 | 21 | 1; 22 | -------------------------------------------------------------------------------- /haskell/knight.cabal: -------------------------------------------------------------------------------- 1 | name: knight 2 | version: 0.1.0.0 3 | -- synopsis: 4 | -- description: 5 | homepage: https://github.com/sampersand/knight#readme 6 | license: BSD3 7 | license-file: LICENSE 8 | author: Sam Westerman 9 | maintainer: sam@sampersand.me 10 | copyright: 2021 Sam Westerman 11 | category: Web 12 | build-type: Simple 13 | cabal-version: >=1.10 14 | extra-source-files: README.md 15 | 16 | executable knight 17 | hs-source-dirs: src 18 | main-is: Main.hs 19 | default-language: Haskell2010 20 | build-depends: base >= 4.7 && < 5 21 | -------------------------------------------------------------------------------- /ruby/boolean.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Kn 4 | class Boolean 5 | def inspect = "Boolean(#{to_s})" 6 | 7 | # note that we call `new` before we redefine it later. 8 | class << (TRUE = new) 9 | def to_i = 1 10 | def to_s = 'true' 11 | def truthy? = true 12 | 13 | def <(rhs) = false 14 | def >(rhs) = !rhs.truthy? 15 | freeze 16 | end 17 | 18 | # note that we call `new` before we redefine it later. 19 | class << (FALSE = new) 20 | def to_i = 0 21 | def to_s = 'false' 22 | def truthy? = false 23 | 24 | def <(rhs) = rhs.truthy? 25 | def >(rhs) = false 26 | freeze 27 | end 28 | 29 | def self.new(bool) = bool ? TRUE : FALSE 30 | end 31 | end -------------------------------------------------------------------------------- /ruby/identifier.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_relative 'error' 3 | 4 | module Kn 5 | class Identifier < BasicObject 6 | @env = {} 7 | 8 | def self.new(ident) 9 | @env[ident] ||= super 10 | end 11 | 12 | attr_reader :ident 13 | attr_writer :value 14 | 15 | def initialize(ident) = @ident = ident.freeze 16 | 17 | def inspect = "Identifier(#@ident)" 18 | 19 | def run 20 | @value or raise ::Kn::RunError, "uninitialized identifier '#@ident'" 21 | end 22 | 23 | # Undefine `==` it so `method_missing` will respond to it. 24 | undef == 25 | 26 | def respond_to_missing?(...) = run.respond_to_missing?(...) 27 | def method_missing(...) = run.public_send(...) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /c/vm/src/main.c: -------------------------------------------------------------------------------- 1 | #include "vm.h" 2 | #include "knight.h" 3 | #include 4 | 5 | int main(int argc, char **argv) { 6 | initialize(); 7 | 8 | execute(argc == 1 ? "DUMP+3 4" : argv[1]); 9 | 10 | // static block_t block = { 11 | // .rc = 0, .length = 6, 12 | // .code = { 13 | // { OP_PUSHL }, { 0 }, 14 | // { OP_PUSHL }, { 0 }, 15 | // { OP_SUB }, 16 | // { OP_DUMP }, 17 | // } 18 | // // { value_new_number(10) }, { OP_PUSHL }, 19 | // }; 20 | // block.code[1] = (bytecode_t) { .value = value_new_string(string_new("12foobar")) }; 21 | // block.code[3] = (bytecode_t) { .value = value_new_number(1) }; 22 | 23 | // vm_t *vm = vm_new(&*bytecode, 6); 24 | // value_free(vm_run(&block)); 25 | } 26 | -------------------------------------------------------------------------------- /c/vm/src/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | #include 5 | 6 | typedef struct { 7 | unsigned length, *rc; 8 | char *str; 9 | } string_t; 10 | 11 | static string_t STRING_TRUE = { 4, NULL, "true" }; 12 | static string_t STRING_FALSE = { 5, NULL, "false" }; 13 | static string_t STRING_NULL = { 4, NULL, "null" }; 14 | static string_t STRING_EMPTY = { 0, NULL, "" }; 15 | static string_t STRING_ZERO = { 1, NULL, "0" }; 16 | static string_t STRING_ONE = { 1, NULL, "1" }; 17 | 18 | string_t *string_tail(string_t *, unsigned); 19 | string_t *string_new(char *); 20 | string_t *string_emplace(char *, unsigned); 21 | 22 | void string_free(string_t *); 23 | string_t *string_clone(string_t *); 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /csharp/Error.cs: -------------------------------------------------------------------------------- 1 | namespace Knight 2 | { 3 | public class KnightException : System.Exception 4 | { 5 | public KnightException(string message) : base(message) { } 6 | public KnightException(string message, System.Exception inner) : base(message, inner) { } 7 | } 8 | 9 | public class RuntimeException : KnightException 10 | { 11 | public RuntimeException(string message) : base(message) { } 12 | public RuntimeException(string message, System.Exception inner) : base(message, inner) { } 13 | } 14 | 15 | public class ParseException : KnightException 16 | { 17 | public ParseException(string message) : base(message) { } 18 | public ParseException(string message, System.Exception inner) : base(message, inner) { } 19 | } 20 | } -------------------------------------------------------------------------------- /csharp/Boolean.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Knight 4 | { 5 | public class Boolean : Literal, IComparable 6 | { 7 | public Boolean(bool data) : base(data) {} 8 | 9 | public static Boolean Parse(Stream stream) { 10 | if (!stream.StartsWith('T', 'F')) 11 | return null; 12 | 13 | var ret = new Boolean(stream.Take() == 'T'); 14 | stream.StripKeyword(); 15 | 16 | return ret; 17 | } 18 | 19 | public override void Dump() => Console.Write($"Boolean({this})"); 20 | 21 | public override string ToString() => _data ? "true" : "false"; 22 | public override bool ToBoolean() => _data; 23 | public override long ToNumber() => _data ? 1 : 0; 24 | 25 | public int CompareTo(IValue obj) => _data.CompareTo(obj.ToBoolean()); 26 | } 27 | } -------------------------------------------------------------------------------- /c/ast-ext/src/parse.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_PARSE_H 2 | #define KN_PARSE_H 3 | 4 | #include "value.h" /* kn_value */ 5 | 6 | /* 7 | * Parses out a value from the given stream, updating the stream to reflect the 8 | * new value. 9 | * 10 | * If no value can be parsed, `KN_UNDEFINED` will be returned. 11 | */ 12 | kn_value kn_parse(const char **stream); 13 | 14 | #ifdef KN_EXT_FUNCTION 15 | /* 16 | * This function is called whenever a keyword function starting with `X` is 17 | * encountered. 18 | * 19 | * The passed `stream` will have only the leading `X` removed, and the function 20 | * should strip the and any other relevant trailing characters before returning. 21 | */ 22 | kn_value kn_parse_extension(const char **stream); 23 | #endif /* KN_EXT_FUNCTION */ 24 | 25 | #endif /* !KN_PARSE_H */ 26 | -------------------------------------------------------------------------------- /c/vm/src/shared.c: -------------------------------------------------------------------------------- 1 | #include /* vfprintf, fprintf */ 2 | #include /* va_list, va_start, va_end */ 3 | #include /* exit, malloc, realloc */ 4 | 5 | #include "shared.h" /* prototypes, size_t */ 6 | 7 | void die(const char *fmt, ...) { 8 | va_list args; 9 | 10 | va_start(args, fmt); 11 | vfprintf(stderr, fmt, args); 12 | va_end(args); 13 | 14 | fprintf(stderr, "\n"); 15 | 16 | exit(1); 17 | } 18 | 19 | void *xmalloc(size_t size) { 20 | void *ptr = malloc(size); 21 | 22 | if (ptr == NULL) 23 | die("malloc failure for size %zd", size); 24 | 25 | return ptr; 26 | } 27 | 28 | void *xrealloc(void *ptr, size_t size) { 29 | ptr = realloc(ptr, size); 30 | 31 | if (ptr == NULL) 32 | die("realloc failure for size %zd", size); 33 | 34 | return ptr; 35 | } 36 | -------------------------------------------------------------------------------- /perl/bin/knight.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | # Not really 'perlish', as you'd generally install Perl modules globally. But, since this isn't really intended to be 7 | # installed globally, I've dynamically loaded the the path to `/lib`. 8 | use File::Basename qw/dirname basename/; 9 | use lib dirname(__FILE__) . '/../lib'; 10 | 11 | use Kn; 12 | 13 | # Prints a usage message and exists. 14 | sub usage { 15 | print STDERR "usage: " . basename(__FILE__) . " (-e program | -f file)\n"; 16 | exit 1; 17 | } 18 | 19 | my $switch = shift @ARGV; 20 | 21 | usage unless $#ARGV == 0 && ($switch eq '-f' || $switch eq '-e'); 22 | 23 | if ($switch eq '-f') { 24 | Kn->run(join '', <>); 25 | } elsif ($switch eq '-e') { 26 | Kn->run(shift @ARGV); 27 | } else { 28 | usage; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /c/c-old/src/shared.c: -------------------------------------------------------------------------------- 1 | #include /* vfprintf, fprintf */ 2 | #include /* va_list, va_start, va_end */ 3 | #include /* exit, malloc, realloc */ 4 | 5 | #include "shared.h" /* prototypes, size_t */ 6 | 7 | void die(const char *fmt, ...) { 8 | va_list args; 9 | 10 | va_start(args, fmt); 11 | vfprintf(stderr, fmt, args); 12 | va_end(args); 13 | 14 | fprintf(stderr, "\n"); 15 | 16 | exit(1); 17 | } 18 | 19 | void *xmalloc(size_t size) { 20 | void *ptr = malloc(size); 21 | 22 | if (ptr == NULL) { 23 | die("malloc failure for size %zd", size); 24 | } 25 | 26 | return ptr; 27 | } 28 | 29 | void *xrealloc(void *ptr, size_t size) { 30 | ptr = realloc(ptr, size); 31 | 32 | if (ptr == NULL) { 33 | die("realloc failure for size %zd", size); 34 | } 35 | 36 | return ptr; 37 | } 38 | -------------------------------------------------------------------------------- /c/c-old/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR ?= src 2 | OBJDIR ?= obj 3 | EXE ?= knight 4 | 5 | CC = gcc 6 | CFLAGS = -Wall -Wextra -Werror -Wpedantic -O3 -DNDEBUG -flto -march=native 7 | override CFLAGS += -F$(SRCDIR) 8 | 9 | ifdef DEBUG 10 | override CFLAGS += -g -fsanitize=address,undefined 11 | else 12 | CEXEFLAGS += -O3 -DNDEBUG -flto -march=native 13 | endif 14 | 15 | objects = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(wildcard $(SRCDIR)/*.c)) 16 | 17 | all: $(EXE) 18 | 19 | optimized: 20 | gcc $(CFLAGS) $(CEXEFLAGS) -o $(EXE) $(wildcard $(SRCDIR)/*.c) 21 | 22 | $(EXE): $(objects) 23 | $(CC) $(CFLAGS) $(CEXEFLAGS) -o $@ $+ 24 | 25 | clean: 26 | -@rm -r $(OBJDIR) 27 | -@rm $(EXE) 28 | 29 | $(OBJDIR): 30 | @mkdir -p $(OBJDIR) 31 | 32 | $(objects): | $(OBJDIR) 33 | 34 | $(OBJDIR)/%.o: $(SRCDIR)/%.c 35 | $(CC) $(CFLAGS) -c $< -o $@ 36 | -------------------------------------------------------------------------------- /perl/lib/Kn/Identifier.pm: -------------------------------------------------------------------------------- 1 | package Kn::Identifier; 2 | use strict; 3 | use warnings; 4 | 5 | use parent 'Kn::Value'; 6 | use Kn::Environment; 7 | 8 | # Parse an identifier from the start of the stream, which must start with a 9 | # lower case letter (or `_`), and then may contain any number of digits, lower 10 | # case letters, or `_`s. 11 | # 12 | # Returns `undef` if the stream doesn't start with an identifier. 13 | sub parse { 14 | my ($class, $stream) = @_; 15 | 16 | $$stream =~ s/\A[a-z_][a-z0-9_]*//p or return; 17 | 18 | $class->new(${^MATCH}); 19 | } 20 | 21 | # Run this argument by fetching its value from the environment. 22 | sub run { 23 | Kn::Environment->get(${shift()}); 24 | } 25 | 26 | # Dumps the class's info. Used for debugging. 27 | sub dump { 28 | 'Identifier(' . ${shift()} . ')'; 29 | } 30 | 31 | 1; 32 | -------------------------------------------------------------------------------- /c/ast/src/ast.c: -------------------------------------------------------------------------------- 1 | #include "ast.h" /* prototypes, kn_ast, kn_value, kn_value_free */ 2 | #include "shared.h" /* xmalloc */ 3 | #include /* free */ 4 | 5 | struct kn_ast *kn_ast_alloc(unsigned argc) { 6 | struct kn_ast *ast = xmalloc( 7 | sizeof(struct kn_ast) + sizeof(kn_value [argc]) 8 | ); 9 | 10 | ast->refcount = 1; 11 | return ast; 12 | } 13 | 14 | struct kn_ast *kn_ast_clone(struct kn_ast *ast) { 15 | ++ast->refcount; 16 | 17 | return ast; 18 | } 19 | 20 | void kn_ast_free(struct kn_ast *ast) { 21 | if (--ast->refcount) // if we're not the last reference, leave early. 22 | return; 23 | 24 | for (unsigned i = 0; i < ast->func->arity; ++i) 25 | kn_value_free(ast->args[i]); 26 | 27 | free(ast); 28 | } 29 | 30 | kn_value kn_ast_run(struct kn_ast *ast) { 31 | return (ast->func->func)(ast->args); 32 | } 33 | -------------------------------------------------------------------------------- /perl/lib/Kn/Number.pm: -------------------------------------------------------------------------------- 1 | package Kn::Number; 2 | use strict; 3 | use warnings; 4 | 5 | use parent 'Kn::Value'; 6 | 7 | # Parses out a `Kn::Number` from the start of a stream. 8 | # A Number is simply a sequence of digits. (The character after the number is 9 | # ignored; `12a` will be parsed as the number 12, and sets the stream to `a`.) 10 | # 11 | # The stream that is passed should be a reference to a string; it will be 12 | # modified in-place if the number parses successfully. 13 | # 14 | # If a number isn't at the start of the stream, the stream is left unmodified 15 | # and `undef` is returned. 16 | sub parse { 17 | my ($class, $stream) = @_; 18 | 19 | $$stream =~ s/\A\d+//p or return; 20 | 21 | $class->new(${^MATCH}); 22 | } 23 | 24 | # Dumps the class's info. Used for debugging. 25 | sub dump { 26 | "Number(${shift()})"; 27 | } 28 | 29 | 1; 30 | -------------------------------------------------------------------------------- /c/ast/src/knight.c: -------------------------------------------------------------------------------- 1 | #include "knight.h" /* prototypes, kn_value, kn_value_run, kn_value_free 2 | KN_UNDEFINED */ 3 | #include "function.h" /* kn_function_startup */ 4 | #include "parse.h" /* kn_parse */ 5 | #include "env.h" /* kn_env_startup, kn_env_shutdown */ 6 | 7 | #ifndef KN_RECKLESS 8 | #include "shared.h" /* die */ 9 | #endif /* !KN_RECKLESS */ 10 | 11 | void kn_startup() { 12 | kn_function_startup(); 13 | kn_env_startup(); 14 | } 15 | 16 | void kn_shutdown() { 17 | kn_env_shutdown(); 18 | } 19 | 20 | kn_value kn_run(const char *stream) { 21 | kn_value parsed = kn_parse(&stream); 22 | 23 | #ifndef KN_RECKLESS 24 | if (parsed == KN_UNDEFINED) 25 | die("unable to parse stream"); 26 | #endif /* !KN_RECKLESS */ 27 | 28 | kn_value ret = kn_value_run(parsed); 29 | kn_value_free(parsed); 30 | return ret; 31 | } 32 | -------------------------------------------------------------------------------- /squire/perl/lib/Types/String.pm: -------------------------------------------------------------------------------- 1 | package String; 2 | 3 | use Value; 4 | use base qw(Value); 5 | 6 | use strict; 7 | use warnings; 8 | use overload 9 | '""' => \&interpolate; 10 | 11 | sub add() { 12 | String->new(shift->{value} + shift->to_string()->{value}); 13 | } 14 | 15 | sub mul() { 16 | String->new(shift->{value} x shift->to_number()->{value}); 17 | } 18 | 19 | sub mod() { 20 | String->new(sprintf shift->{value}, shift->{value}); 21 | } 22 | 23 | sub cmp() { 24 | Number->new(shift->{value} cmp shift->to_string()->{value}); 25 | } 26 | 27 | sub to_bool { 28 | shift->to_number()->to_bool() 29 | } 30 | 31 | sub to_number { 32 | Number->new(shift->{value}); 33 | } 34 | 35 | sub to_array { 36 | Array->new(map &String->new, split('', shift->{value})); 37 | } 38 | 39 | sub interpolate { 40 | '"' . quotemeta(shift->{value}) . '"'; 41 | } 42 | 43 | 1; 44 | -------------------------------------------------------------------------------- /perl/lib/Kn/Null.pm: -------------------------------------------------------------------------------- 1 | package Kn::Null; 2 | use strict; 3 | use warnings; 4 | 5 | use parent 'Kn::Value'; 6 | 7 | use overload 8 | '0+' => sub { 0 }, 9 | '""' => sub { 'null' }; 10 | 11 | my $null; 12 | # Unlike every other value, `Null`s do not take arguments. 13 | sub new { 14 | bless \$null, shift 15 | } 16 | 17 | # Parses a null from the stream, which must start with `N`, and then may include 18 | # any number of upper case letters. 19 | # 20 | # Returns `undef` if the stream doesn't start with null. 21 | sub parse { 22 | my ($class, $stream) = @_; 23 | 24 | $$stream =~ s/\AN[A-Z]*//p or return; 25 | 26 | $class->new(); 27 | } 28 | 29 | # You are not allowed to compare null. 30 | sub cmp { 31 | die "Comparing by null is not allowed."; 32 | } 33 | 34 | 35 | # Dumps the class's info. Used for debugging. 36 | sub dump { 37 | 'Null()'; 38 | } 39 | 40 | 1; 41 | -------------------------------------------------------------------------------- /cpp/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "knight.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace kn; 7 | 8 | void usage(char const* program) { 9 | std::cerr << "usage: " << program << " (-e 'expression' | -f file)" << std::endl; 10 | exit(1); 11 | } 12 | 13 | int main(int argc, char **argv) { 14 | if (argc != 3) { 15 | usage(argv[0]); 16 | } 17 | 18 | initialize(); 19 | 20 | try { 21 | if (std::string_view("-e") == argv[1]) { 22 | run(argv[2]); 23 | } else if (std::string_view("-f") == argv[1]) { 24 | std::ifstream file(argv[2]); 25 | std::ostringstream contents; 26 | contents << file.rdbuf(); 27 | run(contents.str()); 28 | } else { 29 | usage(argv[0]); 30 | } 31 | } catch (std::exception& err) { 32 | std::cerr << "error with your code: " << err.what() << std::endl; 33 | return 1; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/fib.kn: -------------------------------------------------------------------------------- 1 | # We need a global variable to keep track of how deeply nested we are. 2 | # This is analogous to having a global "stack of n"s. 3 | ; = _fib_recur 0 4 | ; = fib BLOCK { 5 | IF (< n 2) { 6 | n # fib(0) and fib(1) = n 7 | } { 8 | # convert fib(n-1) + fib(n-2) into fib(n) + fib(n-1) 9 | ; = n (- n 1) 10 | # Push the value of `n - 1` onto the "stack of `n`s" 11 | ; EVAL (++ "=_fib_stack_" _fib_recur "- n 1") 12 | ; = _fib_recur (+ _fib_recur 1) 13 | 14 | # Get the result of `fib(n)` (ie `fib(n-1)`, as we converted earlier) 15 | ; = tmp (CALL fib) 16 | # Pop the `n - 1` off the stack and assign it to `n`. 17 | ; = _fib_recur (- _fib_recur 1) 18 | ; = n EVAL (+ "_fib_stack_" _fib_recur) 19 | # return `tmp + fib(n-1)` (ie `fib(n-2)`, as we converted earlier) 20 | : + tmp (CALL fib) 21 | } 22 | } 23 | 24 | ; = n 10 25 | : OUTPUT CALL fib 26 | -------------------------------------------------------------------------------- /c/ast-ext/src/ast.c: -------------------------------------------------------------------------------- 1 | #include "ast.h" /* prototypes, kn_ast, kn_value_free, bool */ 2 | #include "shared.h" /* xmalloc */ 3 | #include /* free */ 4 | 5 | struct kn_ast *kn_ast_alloc(unsigned argc) { 6 | struct kn_ast *ast = xmalloc( 7 | sizeof(struct kn_ast) + sizeof(kn_value [argc])); 8 | 9 | #ifdef KN_DYNMAIC_ARGC 10 | ast->argc = argc; 11 | #endif /* KN_DYNMAIC_ARGC */ 12 | 13 | return ast; 14 | } 15 | 16 | struct kn_ast *kn_ast_clone(struct kn_ast *ast) { 17 | ++ast->refcount; 18 | 19 | return ast; 20 | } 21 | 22 | void kn_ast_free(struct kn_ast *ast) { 23 | if (--ast->refcount) // if we're not the last reference, leave early. 24 | return; 25 | 26 | for (unsigned i = 0; i < KN_AST_ARITY(ast); ++i) 27 | kn_value_free(ast->args[i]); 28 | 29 | free(ast); 30 | } 31 | 32 | kn_value kn_ast_run(struct kn_ast *ast) { 33 | return (ast->func->func)(ast->args); 34 | } 35 | -------------------------------------------------------------------------------- /raku/lib/Knight/NonIdempotent.rakumod: -------------------------------------------------------------------------------- 1 | use Knight::Value; 2 | 3 | #| A role used to indicate that, when run, a value may yield different results. 4 | unit role Knight::NonIdempotent; 5 | 6 | #| NonIdempotent types must implement `run`. 7 | method run(--> Knight::Value) { … } 8 | 9 | #| Runs `self` and compares it against `$rhs`. 10 | method cmp(Knight::Value $rhs, --> Order) { 11 | $.run.cmp: $rhs 12 | } 13 | 14 | #| Runs `self` and checks to see if it's return value is equal to `$rhs`. 15 | multi method eql(::?CLASS $rhs, --> Bool) { 16 | $.run.eql: $rhs 17 | } 18 | 19 | #| Runs `self` and converts the result to a `Str`. 20 | method Str(--> Str) { 21 | ~$.run 22 | } 23 | 24 | #| Runs `self` and converts the result to a `Bool`. 25 | method Bool(--> Bool) { 26 | ?$.run 27 | } 28 | 29 | #| Runs `self` and converts the result to an `Int`. 30 | method Int(--> Int) { 31 | +$.run 32 | } 33 | 34 | -------------------------------------------------------------------------------- /cpp/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR?=src 2 | OBJDIR?=obj 3 | EXE?=knight 4 | CXX=g++ 5 | 6 | CXXFLAGS+=-Wall -Wextra -Wpedantic -std=c++17 7 | override CXXFLAGS+=-F$(SRCDIR) 8 | 9 | ifdef DEBUG 10 | override CXXFLAGS+=-g -fsanitize=address,undefined 11 | else 12 | override CXXFLAGS+=-O2 13 | endif 14 | 15 | ifdef OPTIMIZED 16 | override CXXFLAGS+=-O3 -DNDEBUG -flto -march=native -fno-stack-protector 17 | endif 18 | 19 | objects=$(patsubst $(SRCDIR)/%.cpp,$(OBJDIR)/%.o,$(wildcard $(SRCDIR)/*.cpp)) 20 | 21 | .PHONY: all optimized clean 22 | 23 | all: $(EXE) 24 | 25 | optimized: 26 | $(CXX) $(CXXFLAGS) -o $(EXE) $(wildcard $(SRCDIR)/*.cpp) 27 | 28 | $(EXE): $(objects) 29 | $(CXX) $(CXXFLAGS) -o $@ $+ 30 | 31 | clean: 32 | -@rm -r $(OBJDIR) 33 | -@rm $(EXE) 34 | 35 | $(OBJDIR): 36 | @mkdir -p $(OBJDIR) 37 | 38 | $(objects): | $(OBJDIR) 39 | 40 | $(OBJDIR)/%.o: $(SRCDIR)/%.cpp 41 | $(CXX) $(CXXFLAGS) -c $< -o $@ 42 | -------------------------------------------------------------------------------- /javascript/src/knight.js: -------------------------------------------------------------------------------- 1 | import { ParseError } from './error.js'; 2 | import { Value } from './value.js'; 3 | import { Stream } from './stream.js'; 4 | 5 | import { } from './bool.js'; 6 | import { Ident } from './ident.js'; 7 | import { Int } from './int.js'; 8 | import { Null } from './null.js'; 9 | import { Str } from './str.js'; 10 | import { Func } from './func.js'; 11 | 12 | // Only KnightError is exported by default. 13 | export { KnightError } from './error.js'; 14 | 15 | /** 16 | * Parses and executes the input as Knight code. 17 | * 18 | * @param {string} input - The string to parse and execute. 19 | * @return {Value} - The result of executing the code. 20 | */ 21 | export function run(input) { 22 | let value = Value.parse(new Stream(input.toString())); 23 | 24 | if (value === null) { 25 | throw new ParseError('No value could be parsed!'); 26 | } else { 27 | return value.run(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /raku/lib/Knight/String.rakumod: -------------------------------------------------------------------------------- 1 | use Knight::TypedValue; 2 | use Knight::Value; 3 | 4 | #| The String class in Knight. 5 | unit class Knight::String does Knight::TypedValue[Str, * cmp *, * eq *]; 6 | 7 | #| Converts `self` to an integer by stripping leading whitespace, then taking as many sequential digits as possible. 8 | #| 9 | #| An empty string, or a string that doesn't begin with digits, is zero. 10 | method Int(--> Int) is pure { 11 | $!value ~~ /^ \s* <[+-]>? \d* /; 12 | +$<> 13 | } 14 | 15 | #| Concatenates `$rhs` to `self`, returning a new String. 16 | method add(Knight::Value $rhs, --> ::?CLASS) { 17 | ::?CLASS.new: $!value ~ $rhs.Str 18 | } 19 | 20 | #| Duplicates `self` by `$rhs` times. 21 | method mul(Knight::Value $rhs, --> ::?CLASS) { 22 | ::?CLASS.new: $!value x $rhs.Int 23 | } 24 | 25 | #| Gets an internal representation of the class; used in debugging. 26 | method gist(--> Str) { 27 | "String($!value)"; 28 | } 29 | -------------------------------------------------------------------------------- /asm/tmp.c: -------------------------------------------------------------------------------- 1 | long tak(long x, long y, long z) { 2 | if (y < x) { 3 | return tak( 4 | tak(x-1, y, z), 5 | tak(y-1, z, x), 6 | tak(z-1, x, y) 7 | ); 8 | } else { 9 | return z; 10 | } 11 | } 12 | 13 | int main(int argc, char **argv) { 14 | printf("%lu\n", tak( 15 | strtol(argv[1], 0, 10), 16 | strtol(argv[2], 0, 10), 17 | strtol(argv[3], 0, 10) 18 | )); 19 | } 20 | 21 | 22 | // void ltoa(long val, char *idx) { 23 | // do { 24 | // long tmp = val; 25 | // val /= 10; 26 | // *(idx++) = (tmp - val * 10); 27 | // } while (val); 28 | // } 29 | 30 | // char *doit(long val) { 31 | // char ret[21]; 32 | // if (val == 0) return "0"; 33 | // char *idx = ret; 34 | // if (val < 0) { *(idx++) = '-'; val = -val; } 35 | // ltoa(val, idx); 36 | // return ret; 37 | // } 38 | 39 | // int main(int argc, char **_) { 40 | // (void) _; 41 | // volatile long x = argc; 42 | // volatile char *y = doit(x); 43 | 44 | // } 45 | -------------------------------------------------------------------------------- /c/ast-ext/src/knight.c: -------------------------------------------------------------------------------- 1 | #include "knight.h" /* prototypes, kn_value, kn_value_run, kn_value_free 2 | KN_UNDEFINED, size_t */ 3 | #include "function.h" /* kn_function_startup */ 4 | #include "parse.h" /* kn_parse */ 5 | #include "shared.h" /* die */ 6 | #include "string.h" /* kn_string_startup, kn_string_shutdown */ 7 | #include "env.h" /* kn_env_startup, kn_env_shutdown */ 8 | 9 | void kn_startup() { 10 | kn_env_startup(); 11 | kn_string_startup(); 12 | kn_function_startup(); 13 | } 14 | 15 | void kn_shutdown() { 16 | kn_env_shutdown(); 17 | kn_string_shutdown(); 18 | } 19 | 20 | kn_value kn_run(const char *stream) { 21 | kn_value parsed = kn_parse(&stream); 22 | 23 | #ifndef KN_RECKLESS 24 | if (parsed == KN_UNDEFINED) 25 | die("unable to parse stream"); 26 | #endif /* !KN_RECKLESS */ 27 | 28 | kn_value ret = kn_value_run(parsed); 29 | kn_value_free(parsed); 30 | return ret; 31 | } 32 | -------------------------------------------------------------------------------- /c/ast/src/knight.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_KNIGHT_H 2 | #define KN_KNIGHT_H 3 | 4 | #include "value.h" /* kn_value */ 5 | 6 | /* 7 | * Begins the Knight interpreter. 8 | * 9 | * This function should be called before any other Knight function. 10 | */ 11 | void kn_startup(void); 12 | 13 | /* 14 | * Frees all memory related to the current running Knight process. 15 | * 16 | * This invalidates _all_ pointers that Knight functions returned (including all 17 | * `kn_value`s). 18 | * 19 | * After this function is run, `kn_startup` must be called again before calling 20 | * any other Knight functions. 21 | */ 22 | void kn_shutdown(void); 23 | 24 | /* 25 | * Executes the given stream as knight code. 26 | * 27 | * Note that any errors that may be caused during the execution of the code will 28 | * simply abort the program (like all exceptions in Knight do). 29 | */ 30 | kn_value kn_run(const char *stream); 31 | 32 | #endif /* !KN_KNIGHT_H */ 33 | -------------------------------------------------------------------------------- /c/ast/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR?=src 2 | OBJDIR?=obj 3 | EXE?=knight 4 | 5 | CC?=gcc 6 | CFLAGS+=-Wall -Wextra -Wpedantic 7 | override CFLAGS+=-F$(SRCDIR) 8 | 9 | ifdef DEBUG 10 | override CFLAGS+=-g -fsanitize=address,undefined 11 | else 12 | override CFLAGS+=-O3 -flto -march=native -DNDEBUG 13 | endif 14 | 15 | 16 | ifdef COMPUTED_GOTOS 17 | override CFLAGS+=-DKN_COMPUTED_GOTOS -Wno-gnu-label-as-value -Wno-gnu-designator 18 | endif 19 | 20 | objects = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(wildcard $(SRCDIR)/*.c)) 21 | 22 | .PHONY: all optimized clean 23 | 24 | all: $(EXE) 25 | 26 | optimized: 27 | $(CC) $(CFLAGS) $(CEXEFLAGS) -o $(EXE) $(wildcard $(SRCDIR)/*.c) 28 | 29 | $(EXE): $(objects) 30 | $(CC) $(CFLAGS) $(CEXEFLAGS) -o $@ $+ 31 | 32 | clean: 33 | -@rm -r $(OBJDIR) 34 | -@rm $(EXE) 35 | 36 | $(OBJDIR): 37 | @mkdir -p $(OBJDIR) 38 | 39 | $(objects): | $(OBJDIR) 40 | 41 | $(OBJDIR)/%.o: $(SRCDIR)/%.c 42 | $(CC) $(CFLAGS) -c $< -o $@ 43 | -------------------------------------------------------------------------------- /c/vm/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR ?= src 2 | OBJDIR ?= obj 3 | EXE ?= knight 4 | 5 | CC = gcc 6 | CFLAGS = -Wall -Wextra -Wpedantic -flto -march=native -Wno-gnu 7 | CFLAGS += $(CER) 8 | override CFLAGS += -F$(SRCDIR) 9 | 10 | ifdef DEBUG 11 | override CFLAGS += -g -fsanitize=address,undefined 12 | else 13 | CEXEFLAGS += -O3 -DNDEBUG 14 | endif 15 | 16 | # ifdef COMPUTED_GOTOS 17 | # override CFLAGS += -DCOMPUTED_GOTOS -Wno-gnu-label-as-value -Wgnu-designator 18 | # endif 19 | 20 | objects = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(wildcard $(SRCDIR)/*.c)) 21 | 22 | all: $(EXE) 23 | 24 | optimized: 25 | $(CC) $(CFLAGS) $(CEXEFLAGS) -o $(EXE) $(wildcard $(SRCDIR)/*.c) 26 | 27 | $(EXE): $(objects) 28 | $(CC) $(CFLAGS) $(CEXEFLAGS) -o $@ $+ 29 | 30 | clean: 31 | -@rm -r $(OBJDIR) 32 | -@rm $(EXE) 33 | 34 | $(OBJDIR): 35 | @mkdir -p $(OBJDIR) 36 | 37 | $(objects): | $(OBJDIR) 38 | 39 | $(OBJDIR)/%.o: $(SRCDIR)/%.c 40 | $(CC) $(CFLAGS) -c $< -o $@ 41 | -------------------------------------------------------------------------------- /c/ast-ext/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR?=src 2 | OBJDIR?=obj 3 | EXE?=knight 4 | 5 | CC?=gcc 6 | CFLAGS+=-Wall -Wextra -Wpedantic 7 | override CFLAGS+=-F$(SRCDIR) 8 | 9 | ifdef DEBUG 10 | override CFLAGS+=-g -fsanitize=address,undefined 11 | else 12 | override CFLAGS+=-O3 -flto -march=native -DNDEBUG 13 | endif 14 | 15 | 16 | ifdef COMPUTED_GOTOS 17 | override CFLAGS+=-DKN_COMPUTED_GOTOS -Wno-gnu-label-as-value -Wno-gnu-designator 18 | endif 19 | 20 | objects = $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(wildcard $(SRCDIR)/*.c)) 21 | 22 | .PHONY: all optimized clean 23 | 24 | all: $(EXE) 25 | 26 | optimized: 27 | $(CC) $(CFLAGS) $(CEXEFLAGS) -o $(EXE) $(wildcard $(SRCDIR)/*.c) 28 | 29 | $(EXE): $(objects) 30 | $(CC) $(CFLAGS) $(CEXEFLAGS) -o $@ $+ 31 | 32 | clean: 33 | -@rm -r $(OBJDIR) 34 | -@rm $(EXE) 35 | 36 | $(OBJDIR): 37 | @mkdir -p $(OBJDIR) 38 | 39 | $(objects): | $(OBJDIR) 40 | 41 | $(OBJDIR)/%.o: $(SRCDIR)/%.c 42 | $(CC) $(CFLAGS) -c $< -o $@ 43 | -------------------------------------------------------------------------------- /ruby/number.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Kn 4 | class Number 5 | include Comparable 6 | 7 | def initialize(num) = @num = num 8 | 9 | def inspect = "Number(#@num)" 10 | 11 | def to_i = @num 12 | def to_s = @num.to_s 13 | def truthy? = @num.nonzero? 14 | 15 | def +(rhs) = Number.new(@num + rhs.to_i) 16 | def -(rhs) = Number.new(@num - rhs.to_i) 17 | def *(rhs) = Number.new(@num * rhs.to_i) 18 | 19 | def /(rhs) 20 | Number.new @num.fdiv(rhs.to_i).truncate 21 | rescue ZeroDivisionError => err 22 | raise RunError, 'cannot divide by zero' 23 | end 24 | 25 | def %(rhs) 26 | Number.new @num % rhs.to_i 27 | rescue ZeroDivisionError => err 28 | raise RunError, 'cannot modulo by zero' 29 | end 30 | 31 | def **(rhs) = Number.new((@num ** rhs.to_i).to_i) 32 | 33 | def <=>(rhs) = @num <=> rhs.to_i 34 | def ==(rhs) = (rhs = rhs.run).is_a?(self.class) && @num == rhs.to_i 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /squire/perl/main.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use lib 'lib'; 4 | 5 | use Lexer; 6 | use Types::Array; 7 | my $x = Lexer->new("abc"); 8 | print $x; 9 | 10 | __END__ 11 | BEGIN { use File::Basename; push @INC, dirname(__FILE__) . '/types'; } 12 | 13 | use Types::Boolean; 14 | use Types::Null; 15 | use Types::Number; 16 | use Types::String; 17 | use Types::Array; 18 | use Lexer; 19 | 20 | 21 | my $lexer = Lexer->new(" ;"); 22 | print $lexer->next(); 23 | # print !!$lexer; 24 | # print $lexer->advance(); 25 | 26 | __END__ 27 | my $num = Number->new(12); 28 | my $arr = Array->new( 29 | String->new('a'), 30 | String->new('b'), 31 | String->new('c'), 32 | Number->new(0), 33 | Number->new(1), 34 | Number->new(2) 35 | ); 36 | 37 | $\="\n"; 38 | print $arr; 39 | $arr->insert(Number->new(2), String->new("abc")); 40 | $arr->set_index(Number->new(9), String->new("?")); 41 | $arr->delete(Number->new(7)); 42 | my $x = "123 a"; 43 | print Number->parse($x); 44 | print $x; 45 | -------------------------------------------------------------------------------- /python/knight/boolean.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from knight import Value, Stream, Literal 3 | from typing import Union 4 | import re 5 | 6 | class Boolean(Literal[bool]): 7 | """ Used to represent boolean types within Knight. """ 8 | 9 | REGEX: re.Pattern = re.compile(r'([TF])[A-Z]*') 10 | 11 | @classmethod 12 | def parse(cls, stream: Stream) -> Union[None, Boolean]: 13 | """ Parses a `Boolean` if the stream starts with `T` or `F`. """ 14 | if match := stream.matches(Boolean.REGEX, 1): 15 | return cls(match == 'T') 16 | else: 17 | return None 18 | 19 | def __str__(self) -> str: 20 | """ Returns `"true"` when true and `"false"` when false. """ 21 | return 'true' if self else 'false' 22 | 23 | def __lt__(self, rhs: Value) -> bool: 24 | """ Checks to see if `self` is falsey and `rhs` is truthy. """ 25 | return not self and rhs 26 | 27 | def __gt__(self, rhs: Value) -> bool: 28 | """ Checks to see if `self` is truthy and `rhs` is falsey. """ 29 | return self and not rhs 30 | -------------------------------------------------------------------------------- /c/c-old/src/knight.c: -------------------------------------------------------------------------------- 1 | #include /* time */ 2 | #include /* srand */ 3 | 4 | #include "ast.h" /* kn_ast_t, kn_ast_parse, kn_ast_run, kn_ast_free */ 5 | #include "knight.h" /* prototypes, kn_value_t */ 6 | #include "env.h" /* kn_env_init */ 7 | 8 | // We define the environment size as starting at 256 identifiers. 9 | #ifndef KNIGHT_ENV_INIT_SIZE 10 | #define KNIGHT_ENV_INIT_SIZE 256 11 | #endif 12 | 13 | int kn_init() { 14 | // Ensure we only initialize knight once. 15 | static int INITIALIZED = 0; 16 | 17 | if (INITIALIZED) { 18 | return 0; 19 | } 20 | 21 | INITIALIZED = 1; 22 | kn_env_init(KNIGHT_ENV_INIT_SIZE); 23 | 24 | // seed the random number generator. 25 | srand(time(NULL)); 26 | 27 | return 1; 28 | } 29 | 30 | void kn_free() { 31 | kn_env_free(); 32 | } 33 | 34 | struct kn_value_t kn_run(const char *stream) { 35 | struct kn_ast_t ast = kn_ast_parse(&stream); 36 | struct kn_value_t ret = kn_ast_run(&ast); 37 | 38 | kn_ast_free(&ast); 39 | 40 | return ret; 41 | } 42 | -------------------------------------------------------------------------------- /c/ast-ext/ext/list.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_EXT_LIST_H 2 | #define KN_EXT_LIST_H 3 | 4 | #include "../src/value.h" 5 | #include "../src/custom.h" 6 | #include 7 | 8 | extern const struct kn_custom_vtable kn_list_vtable; 9 | 10 | struct kn_list { 11 | kn_value data; 12 | int refcount; 13 | struct kn_list *next; 14 | }; 15 | 16 | struct kn_list kn_list_empty = { 17 | .data = KN_UNDEFINED, 18 | .refcount = -1, 19 | .next = &kn_list_empty 20 | }; 21 | 22 | struct kn_list *kn_list_new(kn_value); 23 | struct kn_list *kn_list_clone(struct kn_list *); 24 | void kn_list_free(struct kn_list *); 25 | void kn_list_dump(const struct kn_list *); 26 | 27 | 28 | bool kn_list_is_empty(const struct kn_list *); 29 | kn_value kn_list_car(struct kn_list *); 30 | struct kn_list *kn_list_cdr(struct kn_list *); 31 | struct kn_list *kn_list_cons(struct kn_list *, struct kn_list *); 32 | struct kn_list *kn_list_run(struct kn_list *); 33 | 34 | kn_value kn_fn_extension_parse(const char **stream); 35 | 36 | #endif /* !KN_EXT_LIST_H */ 37 | 38 | -------------------------------------------------------------------------------- /c/vm/src/shared.h: -------------------------------------------------------------------------------- 1 | #ifndef SHARED_H 2 | #define SHARED_H 3 | 4 | #include /* size_t */ 5 | 6 | /* 7 | * A function that's used to halt the execution of the program, writing the 8 | * given message to stderr before exiting with code 1. 9 | * 10 | * Since this aborts the program, it's marked both `noreturn` and `cold`. 11 | */ 12 | void die(const char *msg, ...) __attribute__((noreturn,cold)); 13 | 14 | /* 15 | * Allocates `size_t` bytes of memory and returns a pointer to it. 16 | * 17 | * This is identical to the stdlib's `malloc`, except the program is aborted 18 | * instead of returning `NULL`. 19 | */ 20 | void *xmalloc(size_t size) __attribute__((malloc)); 21 | 22 | /* 23 | * Resizes the pointer to a segment of at least `size_t` bytes of memory and, 24 | * returning the new segment's pointer. 25 | * 26 | * This is identical to the stdlib's `realloc`, except the program is aborted 27 | * instead of returning `NULL`. 28 | */ 29 | void *xrealloc(void *ptr, size_t size); 30 | 31 | #endif /* SHARED_H */ 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Sam Westerman 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /csharp/Identifier.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | 4 | namespace Knight 5 | { 6 | public class Identifier : NonIdempotent, IValue 7 | { 8 | private static IDictionary ENV = new Dictionary(); 9 | 10 | private string _name; 11 | 12 | public Identifier(string name) => _name = name; 13 | 14 | public static Identifier Parse(Stream stream) { 15 | bool isLower(char c) => char.IsLower(c) || c == '_'; 16 | 17 | var contents = stream.TakeWhileIfStartsWith(isLower, c => isLower(c) || char.IsDigit(c)); 18 | 19 | return contents == null ? null : new Identifier(contents); 20 | } 21 | 22 | public override void Dump() => Console.Write($"Identifier({_name})"); 23 | 24 | public override IValue Run() { 25 | IValue result; 26 | 27 | if (ENV.TryGetValue(_name, out result)){ 28 | return result; 29 | } else { 30 | throw new RuntimeException($"Unknown identifier '{_name}'."); 31 | } 32 | } 33 | 34 | public void Assign(IValue value) => ENV[_name] = value; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /raku/lib/Knight/Null.rakumod: -------------------------------------------------------------------------------- 1 | use Knight::Value; 2 | use Knight::TypedValue; 3 | 4 | #| The "null" type within Knight. 5 | unit class Knight::Null does Knight::Value; 6 | 7 | #| The sole instance of this class. 8 | my ::?CLASS \INSTANCE = ::?CLASS.bless; # ::?CLASS.bless; 9 | 10 | #| Creates a new Null instance. 11 | method new(--> ::?CLASS) is pure { INSTANCE } 12 | 13 | #| Simply returns `"null"`. 14 | method Str(--> 'null') is pure { } 15 | 16 | #| Simply returns `0`. 17 | method Int(--> 0) is pure { } 18 | 19 | #| Simply returns `False`. 20 | method Bool(--> False) is pure { } 21 | 22 | #| Comparison to Null is invalid. 23 | #| 24 | #| If attempted the program will `die`. 25 | method cmp(Knight::Value $, --> Order) { 26 | die 'Cannot compare Null.'; 27 | } 28 | 29 | #| All Nulls are equal to eachother. 30 | multi method eql(::?CLASS $, --> True) is pure { } 31 | 32 | #| Running a `Null` simply returns itself. 33 | method run(--> Knight::Value) is pure { self } 34 | 35 | #| Gets an internal representation of the class; used in debugging. 36 | method gist(--> Str) { 37 | "Null()"; 38 | } 39 | -------------------------------------------------------------------------------- /php/src/Knight.php: -------------------------------------------------------------------------------- 1 | run(); 20 | } 21 | 22 | // I don't have composer installed... So I used this hack instead. 23 | require_once __DIR__ . '/Stream.php'; 24 | require_once __DIR__ . '/Nil.php'; 25 | require_once __DIR__ . '/Number.php'; 26 | require_once __DIR__ . '/Str.php'; 27 | require_once __DIR__ . '/Boolean.php'; 28 | require_once __DIR__ . '/Identifier.php'; 29 | require_once __DIR__ . '/Func.php'; 30 | -------------------------------------------------------------------------------- /c/ast-ext/src/knight.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_KNIGHT_H 2 | #define KN_KNIGHT_H 3 | 4 | #include /* size_t */ 5 | #include "value.h" /* kn_value */ 6 | 7 | /* 8 | * Begins the Knight interpreter. 9 | * 10 | * This function should be called before any other Knight function. 11 | * 12 | * Note that if the capacity is zero, the default capacity (defined by 13 | * `KN_ENV_DEFAULT_CAPACITY`, which can be overwritten). 14 | */ 15 | void kn_startup(void); 16 | 17 | /* 18 | * Frees all memory related to the current running Knight process. 19 | * 20 | * This invalidates _all_ pointers that Knight functions returned (including all 21 | * `kn_value`s). 22 | * 23 | * After this function is run, `kn_startup` must be called again before calling 24 | * any other Knight functions. 25 | */ 26 | void kn_shutdown(void); 27 | 28 | /* 29 | * Executes the given stream as knight code. 30 | * 31 | * Note that any errors that may be caused during the execution of the code will 32 | * simply abort the program (like all exceptions in Knight do). 33 | */ 34 | kn_value kn_run(const char *stream); 35 | 36 | #endif /* !KN_KNIGHT_H */ 37 | -------------------------------------------------------------------------------- /asm/env.s: -------------------------------------------------------------------------------- 1 | .include "debug.s" 2 | 3 | .bss 4 | kn_env_data: 5 | kn_env_capacity: 6 | .quad 0 7 | kn_env_length: 8 | .quad 0 9 | kn_env_keys: 10 | .quad 0 11 | kn_env_vals: 12 | .quad 0 13 | 14 | .text 15 | /* 16 | struct kn_env_t { 17 | size_t capacity; 18 | size_t length; 19 | 20 | const char **keys; 21 | struct kn_value_t *vals; 22 | }; 23 | 24 | // The singleton value of `kn_env_t`. 25 | static struct kn_env_t KN_ENV; 26 | */ 27 | 28 | .globl kn_env_initialize 29 | kn_env_initialize: 30 | push %rbx 31 | mov %rdi, %rbx 32 | movq %rdi, kn_env_capacity(%rip) 33 | imul $8, %rbx 34 | mov %rbx, %rdi 35 | call xmalloc 36 | mov %rax, kn_env_keys(%rip) 37 | mov %rbx, %rdi 38 | call xmalloc 39 | mov %rax, kn_env_vals(%rip) 40 | pop %rbx 41 | ret 42 | 43 | .globl kn_env_get 44 | kn_env_get: 45 | todo "kn_env_get" 46 | // NOTE: if the value does not exist, crash. 47 | 48 | .globl kn_env_set 49 | kn_env_set: 50 | push %rbx 51 | push %r12 52 | push %r13 53 | mov %rdi, %r12 54 | mov %rsi, %r13 55 | xor %ebx, %ebx 56 | 57 | cmp %ebx, kn_env_length(%rip) 58 | 59 | 60 | pop %r13 61 | pop %r12 62 | pop %rbx 63 | ret 64 | -------------------------------------------------------------------------------- /asm/old/env.s: -------------------------------------------------------------------------------- 1 | .include "debug.s" 2 | 3 | .bss 4 | kn_env_data: 5 | kn_env_capacity: 6 | .quad 0 7 | kn_env_length: 8 | .quad 0 9 | kn_env_keys: 10 | .quad 0 11 | kn_env_vals: 12 | .quad 0 13 | 14 | .text 15 | /* 16 | struct kn_env_t { 17 | size_t capacity; 18 | size_t length; 19 | 20 | const char **keys; 21 | struct kn_value_t *vals; 22 | }; 23 | 24 | // The singleton value of `kn_env_t`. 25 | static struct kn_env_t KN_ENV; 26 | */ 27 | 28 | .globl kn_env_initialize 29 | kn_env_initialize: 30 | push %rbx 31 | mov %rdi, %rbx 32 | movq %rdi, kn_env_capacity(%rip) 33 | imul $8, %rbx 34 | mov %rbx, %rdi 35 | call xmalloc 36 | mov %rax, kn_env_keys(%rip) 37 | mov %rbx, %rdi 38 | call xmalloc 39 | mov %rax, kn_env_vals(%rip) 40 | pop %rbx 41 | ret 42 | 43 | .globl kn_env_get 44 | kn_env_get: 45 | todo "kn_env_get" 46 | // NOTE: if the value does not exist, crash. 47 | 48 | .globl kn_env_set 49 | kn_env_set: 50 | push %rbx 51 | push %r12 52 | push %r13 53 | mov %rdi, %r12 54 | mov %rsi, %r13 55 | xor %ebx, %ebx 56 | 57 | cmp %ebx, kn_env_length(%rip) 58 | 59 | 60 | pop %r13 61 | pop %r12 62 | pop %rbx 63 | ret 64 | -------------------------------------------------------------------------------- /squire/perl/lib/Types/Value.pm: -------------------------------------------------------------------------------- 1 | package Types::Value; 2 | use strict; 3 | use warnings; 4 | 5 | use overload 6 | '""' => \&interpolate; 7 | 8 | # Creates a new `Value` (or whatever its subclass is) by simply accepting 9 | # whatever value's given 10 | sub new { 11 | my $class = shift; 12 | my $self = bless { value => shift }, $class; 13 | $self->_validate(); 14 | $self; 15 | } 16 | 17 | # Validate that the value is correctly constructed. 18 | # By default, all types are. 19 | sub _validate { 20 | 1; 21 | } 22 | 23 | # Converts this type to a String by simply using `value` as a type. 24 | sub to_string { 25 | String->new(shift->{value}); 26 | } 27 | 28 | # All types are truthy by default. 29 | sub to_bool { 30 | Boolean->new(1); 31 | } 32 | 33 | # Interpolation simply calls `to_string` and gets its value. 34 | sub interpolate { 35 | shift->to_string()->{value}; 36 | } 37 | 38 | # Logical negation of this value. 39 | sub not { 40 | shift->to_bool()->not(); 41 | } 42 | 43 | # Checks to see if two values are equal. This default only works for 44 | 45 | sub eql { 46 | Boolean->new(shift->cmp(shift)->{value} == 0) 47 | } 48 | 49 | 1; 50 | -------------------------------------------------------------------------------- /haskell/src/Evaluator.hs: -------------------------------------------------------------------------------- 1 | module Evaluator where 2 | 3 | import Knight.Types 4 | import Data.Map 5 | 6 | import Control.Applicative 7 | import Control.Monad.IO.Class 8 | 9 | type Env = Map String Value 10 | newtype EvaluatorM a = EvaluatorM { eval :: Env -> IO (Env, Either String a) } 11 | type Evaluator = EvaluatorM Value 12 | 13 | -- {- Implementation for `EvaluatorM`. -} 14 | instance Functor EvaluatorM where 15 | fmap f (EvaluatorM m) = EvaluatorM $ \env -> do 16 | (env', res) <- m env 17 | pure (env', f <$> res) 18 | 19 | instance Applicative EvaluatorM where 20 | pure val = EvaluatorM $ \env -> pure (env, Right val) 21 | 22 | EvaluatorM pf <*> EvaluatorM px = EvaluatorM $ \env -> do 23 | (env', mf) <- pf env 24 | (env'', mx) <- px env' 25 | pure $ (env'', mf <*> mx) 26 | 27 | instance Monad EvaluatorM where 28 | EvaluatorM lhs >>= f = EvaluatorM $ \env -> do 29 | (env', val) <- lhs env 30 | case val of 31 | Left l -> pure (env', Left l) 32 | Right r -> let EvaluatorM rhs = f r in rhs env' 33 | 34 | instance MonadIO EvaluatorM where 35 | liftIO io = EvaluatorM $ \env -> (,) env <$> Right <$> io 36 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | // #![warn(missing_docs, missing_doc_code_examples)] 2 | #![allow(clippy::tabs_in_doc_comments, unused)] 3 | #![warn(/*, missing_doc_code_examples, missing_docs*/)] 4 | 5 | pub mod function; 6 | pub mod rcstring; 7 | mod value; 8 | mod error; 9 | mod stream; 10 | pub mod environment; 11 | 12 | /// The number type within Knight. 13 | pub type Number = i64; 14 | 15 | #[doc(inline)] 16 | pub use rcstring::RcString; 17 | 18 | #[doc(inline)] 19 | pub use function::Function; 20 | 21 | pub use stream::Stream; 22 | pub use environment::{Environment, Variable}; 23 | pub use value::Value; 24 | pub use error::{ParseError, RuntimeError}; 25 | 26 | /// Runs the given string as Knight code, returning the result of its execution. 27 | pub fn run_str>(input: S, env: &mut Environment<'_, '_, '_>) -> Result { 28 | run(input.as_ref().chars(), env) 29 | } 30 | 31 | /// Parses a [`Value`] from the given iterator and then runs the value. 32 | pub fn run(input: I, env: &mut Environment<'_, '_, '_>) -> Result 33 | where 34 | I: IntoIterator 35 | { 36 | Value::parse(input, env)?.run(env) 37 | } 38 | -------------------------------------------------------------------------------- /c/c-old/src/knight.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_H 2 | #define KN_H 3 | 4 | #include "value.h" /* kn_value_t */ 5 | 6 | /* 7 | * Initializes Knight. 8 | * 9 | * This function should be called before any other Knight-related functions are 10 | * executed. It may be called multiple times, but only the first time actually 11 | * initializes knight; subsequent calls are ignored. 12 | * 13 | * This returns `1` if this is the first time being initialized, and `0` if it 14 | * has been initialized before. 15 | */ 16 | int kn_init(void); 17 | 18 | /* 19 | * Frees all resources associated with knight Knight. 20 | * 21 | * Technically, this function is not needed. But it's a way to prove to valgrind 22 | * that i don't actually have unreachable memory. 23 | */ 24 | void kn_free(void); 25 | 26 | /* 27 | * Parses and runs the given string in the global namespace. 28 | * 29 | * If any errors occur during the parsing or execution of the string, the 30 | * program is aborted. 31 | * 32 | * As all variables are global, any changes made to variables will be visible 33 | * to every other call to `kn_run`. 34 | */ 35 | struct kn_value_t kn_run(const char *stream); 36 | 37 | #endif /* KN_H */ 38 | -------------------------------------------------------------------------------- /cpp/src/variable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "knight.hpp" 4 | #include "value.hpp" 5 | #include 6 | 7 | namespace kn { 8 | // A variable within Knight. 9 | // 10 | // As per the Knight specs, all variables are global. 11 | class Variable { 12 | // The name of the variable. 13 | std::string const name; 14 | 15 | // The value associated with this variable. 16 | std::optional value; 17 | public: 18 | 19 | // Creates a new Variable with the given name. 20 | explicit Variable(std::string name) noexcept; 21 | 22 | // There is no default variable. 23 | Variable() = delete; 24 | 25 | // Parses an Variable out, or returns `nullptr` if the first character isn't a letter or `_`. 26 | static std::optional parse(std::string_view& view); 27 | 28 | // Runs the variable, looking up its last assigned value. 29 | // 30 | // Throws an `Error` if the variable was never assigned. 31 | Value run(); 32 | 33 | // Provides debugging output of this type. 34 | std::ostream& dump(std::ostream& out) const noexcept; 35 | 36 | // Assigns a value to this variable, discarding its previous value. 37 | void assign(Value newvalue) noexcept; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /c/c-old/src/env.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_ENV_H 2 | #define KN_ENV_H 3 | 4 | #include "value.h" /* kn_value_t */ 5 | 6 | /* 7 | * Initialize the environment with the starting capacity. 8 | * 9 | * `capacity` must be less than or equal to `SIZE_MAX / sizeof(kn_value_t)`, 10 | * and may not be zero. 11 | * 12 | * This must be run before any Knight code is executed. 13 | */ 14 | void kn_env_init(size_t capacity); 15 | 16 | /* 17 | * Frees all memory associated with the environment 18 | */ 19 | void kn_env_free(void); 20 | 21 | /* 22 | * Gets the value associated with the given identifier. 23 | * 24 | * If the identifier is unknown, `NULL` is returned. 25 | * 26 | * The identifier must not be `NULL`. Additionally, the caller must not call 27 | * `kn_value_free` on the returned value. 28 | */ 29 | const struct kn_value_t *kn_env_get(const char *identifier); 30 | 31 | /* 32 | * Assigns an identifier to a value. 33 | * 34 | * The identifier must not be `NULL`. The value passed to this function 35 | * must not be freed by anyone else---it's "ownership" is passed to the 36 | * environment. 37 | */ 38 | void kn_env_set(const char *identifier, struct kn_value_t value); 39 | 40 | #endif /* KN_ENV_H */ 41 | -------------------------------------------------------------------------------- /c/vm/src/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include "shared.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | string_t *string_tail(string_t *string, unsigned start) { 9 | string_t *result = xmalloc(sizeof(string_t)); 10 | 11 | result->length = string->length - start; 12 | result->rc = string->rc; 13 | result->str = string->str + start; 14 | string_clone(string); 15 | 16 | return result; 17 | } 18 | 19 | string_t *string_emplace(char *str, unsigned length) { 20 | if (length == 0) { 21 | assert(strlen(str) == 0); 22 | return &STRING_EMPTY; 23 | } 24 | 25 | string_t *string = xmalloc(sizeof(string_t)); 26 | 27 | string->length = length; 28 | string->rc = xmalloc(sizeof(unsigned)); 29 | string->str = str; 30 | 31 | return string; 32 | } 33 | 34 | string_t *string_new(char *str) { 35 | return string_emplace(str, strlen(str)); 36 | } 37 | 38 | void string_free(string_t *string) { 39 | if (string->rc != NULL && !--*string->rc) { 40 | free(string->str); 41 | free(string); 42 | } 43 | } 44 | 45 | string_t *string_clone(string_t *string) { 46 | if (string->rc != NULL) 47 | ++*string->rc; 48 | 49 | return string; 50 | } 51 | -------------------------------------------------------------------------------- /squire/perl/lib/Types/Number.pm: -------------------------------------------------------------------------------- 1 | package Number; 2 | 3 | use Value; 4 | use base qw(Value); 5 | 6 | use strict; 7 | use warnings; 8 | 9 | sub add() { Number->new(shift->{value} + shift->to_number()->{value}); } 10 | sub sub() { Number->new(shift->{value} - shift->to_number()->{value}); } 11 | sub mul() { Number->new(shift->{value} * shift->to_number()->{value}); } 12 | sub div() { Number->new(shift->{value} / shift->to_number()->{value}); } 13 | sub mod() { Number->new(shift->{value} % shift->to_number()->{value}); } 14 | sub pow() { Number->new(shift->{value} **shift->to_number()->{value}); } 15 | 16 | sub band(){ Number->new(shift->{value} & shift->to_number()->{value}); } 17 | sub bor() { Number->new(shift->{value} | shift->to_number()->{value}); } 18 | sub bxor(){ Number->new(shift->{value} ^ shift->to_number()->{value}); } 19 | sub shl() { Number->new(shift->{value}<<(shift->to_number()->{value}));} 20 | sub shr() { Number->new(shift->{value} >>shift->to_number()->{value}); } 21 | sub cmp() { Number->new(shift->{value}<=>shift->to_number()->{value}); } 22 | 23 | sub to_number { shift; } 24 | sub to_boolean{ Boolean->new(shift->{value} != 0); } 25 | 26 | sub parse { 27 | $_[1] =~ s/\A\d+\b//p and Number->new(${^MATCH}) 28 | } 29 | 30 | 1; 31 | -------------------------------------------------------------------------------- /raku/lib/Knight/Identifier.rakumod: -------------------------------------------------------------------------------- 1 | use Knight::Value; 2 | use Knight::NonIdempotent; 3 | 4 | #| An identifier within Knight. 5 | #| 6 | #| As per the Knight specs, all variables are global. 7 | unit class Knight::Identifier does Knight::Value does Knight::NonIdempotent; 8 | 9 | #| The list of all known identifiers. 10 | my %ALL; 11 | 12 | #| The identifier for this string. 13 | has Str $!ident is built; 14 | 15 | #| Creates a new identifier with the given variable name. 16 | method new(Str $ident, --> ::?CLASS) { 17 | self.bless :$ident 18 | } 19 | 20 | #| Fetches the value associated with this identifier. 21 | #| 22 | #| If the identifier hasn't been assigned before, the program `die`s. 23 | method run(--> Knight::Value) { 24 | die "unknown variable '$!ident'" unless $!ident ~~ %ALL; 25 | 26 | %ALL{$!ident} 27 | } 28 | 29 | #| Assigns a value to this identifier. 30 | #| 31 | #| Note that `$value` is evaluated. 32 | method assign(Knight::Value $value, --> Knight::Value) { 33 | my $result = $value.run; 34 | 35 | %ALL{$!ident} = $result; # needs to be evaluated so `= a O 3` will have `3` printed. 36 | 37 | $result 38 | } 39 | 40 | #| Gets an internal representation of the class; used in debugging. 41 | method gist(--> Str) { 42 | "Identifier($!ident)"; 43 | } 44 | -------------------------------------------------------------------------------- /haskell/src/Knight/Types.hs: -------------------------------------------------------------------------------- 1 | {- | The types defined within Knight -} 2 | module Knight.Types where 3 | 4 | -- | A value in Knight. 5 | -- 6 | -- Because Knight's entirely expression-based, everything is a Value. 7 | data Value 8 | = Nullary NullaryFn 9 | | Unary UnaryFn Value 10 | | Binary BinaryFn Value Value 11 | | Ternary TernaryFn Value Value Value 12 | deriving (Show, Eq) 13 | 14 | -- | A literal within Knight. 15 | -- 16 | -- These can be thought of as functions that take no arguments. 17 | data Literal 18 | = Null 19 | | Bool Bool 20 | | Num Integer 21 | | Text String 22 | deriving (Show, Eq) 23 | 24 | data NullaryFn 25 | = Literal Literal 26 | | Variable String 27 | | Prompt 28 | deriving (Show, Eq) 29 | 30 | -- | Functions that takes exactly one argument. 31 | data UnaryFn 32 | = Not 33 | | FnDef 34 | | Call 35 | | Output 36 | | Quit 37 | | Eval 38 | | System 39 | deriving (Show, Eq) 40 | 41 | -- | Functions that takes exactly two arguments. 42 | data BinaryFn 43 | = Random 44 | | While 45 | | Endl | Assign 46 | | Add | Sub | Mul | Div | Mod | Pow 47 | | Lth | Gth | And | Or 48 | deriving (Show, Eq) 49 | 50 | -- | Functions that takes exactly three arguments. 51 | data TernaryFn 52 | = If 53 | deriving (Show, Eq) 54 | -------------------------------------------------------------------------------- /python/knight/value.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | from knight import Stream 4 | from typing import Union 5 | 6 | TYPES = [] 7 | 8 | class Value(): 9 | """ 10 | The type in Knight that represents any representable entity. 11 | """ 12 | 13 | @staticmethod 14 | def parse(stream: Stream) -> Union[None, Value]: 15 | """ 16 | Parses a value out of the `stream`, or returns `None` if 17 | nothing can be parsed. 18 | """ 19 | stream.strip() 20 | 21 | for cls in TYPES: 22 | value = cls.parse(stream) 23 | 24 | if value is not None: 25 | return value 26 | 27 | return None 28 | 29 | def __init_subclass__(cls, parse: bool =True, **rest): 30 | """ Adds `cls` to the list of classes to parse. """ 31 | super().__init_subclass__(**rest) 32 | 33 | if parse: 34 | TYPES.append(cls) 35 | 36 | def run(self) -> Value: 37 | """ Return the result of running this value. """ 38 | raise NotImplemented 39 | 40 | def __int__(self) -> int: 41 | """ Converts this class to an integer. """ 42 | return int(self.run()) 43 | 44 | def __str__(self) -> str: 45 | """ Converts this class to a string. """ 46 | return str(self.run()) 47 | 48 | def __bool__(self) -> bool: 49 | """ Converts this class to a boolean. """ 50 | return bool(self.run()) 51 | -------------------------------------------------------------------------------- /c/ast/src/ast.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_AST_H 2 | #define KN_AST_H 3 | 4 | #include "function.h" /* kn_function */ 5 | #include "value.h" /* kn_value */ 6 | 7 | /* 8 | * The type that represents a function and its arguments in Knight. 9 | * 10 | * Note that this struct should be passed to `kn_ast_free` to release its 11 | * resources. 12 | */ 13 | struct kn_ast { 14 | /* 15 | * The function associated with this ast. 16 | */ 17 | const struct kn_function *func; 18 | 19 | /* 20 | * How many references to this object exist. 21 | */ 22 | unsigned refcount; 23 | 24 | /* 25 | * The arguments of this ast. 26 | */ 27 | kn_value args[]; 28 | }; 29 | 30 | /* 31 | * Allocates a new `kn_ast` with the given number of arguments. 32 | */ 33 | struct kn_ast *kn_ast_alloc(unsigned argc); 34 | 35 | /* 36 | * Duplicates the ast, returning a new value that must be `kn_ast_free`d 37 | * independently from the passed `ast`. 38 | */ 39 | struct kn_ast *kn_ast_clone(struct kn_ast *ast); 40 | 41 | /* 42 | * Releases the memory resources associated with this struct. 43 | */ 44 | void kn_ast_free(struct kn_ast *ast); 45 | 46 | /* 47 | * Executes a `kn_ast`, returning the value associated with its execution. 48 | */ 49 | kn_value kn_ast_run(struct kn_ast *ast); 50 | 51 | #endif /* !KN_AST_H */ 52 | -------------------------------------------------------------------------------- /c/vm/src/value.h: -------------------------------------------------------------------------------- 1 | #ifndef VALUE_H 2 | #define VALUE_H 3 | 4 | #include 5 | #include 6 | #include "string.h" 7 | 8 | struct _variable_t; 9 | struct _blockptr_t; 10 | 11 | typedef uint64_t value_t; 12 | typedef int64_t number_t; 13 | typedef bool boolean_t; 14 | 15 | #define FALSE_ 0 16 | #define NULL_ 2 17 | #define TRUE_ 4 18 | #define UNDEFINED 8 19 | 20 | value_t value_new_number(number_t); 21 | value_t value_new_boolean(boolean_t); 22 | value_t value_new_string(string_t *); 23 | value_t value_new_variable(struct _variable_t *); 24 | value_t value_new_block(struct _blockptr_t *); 25 | 26 | bool value_is_number(value_t); 27 | bool value_is_boolean(value_t); 28 | bool value_is_string(value_t); 29 | bool value_is_variable(value_t); 30 | bool value_is_block(value_t); 31 | 32 | number_t value_as_number(value_t); 33 | boolean_t value_as_boolean(value_t); 34 | string_t *value_as_string(value_t); 35 | struct _variable_t *value_as_variable(value_t); 36 | struct _blockptr_t *value_as_block(value_t); 37 | 38 | number_t value_to_number(value_t); 39 | boolean_t value_to_boolean(value_t); 40 | string_t *value_to_string(value_t); 41 | 42 | void value_dump(value_t); 43 | 44 | value_t value_clone(value_t); 45 | value_t value_run(value_t); 46 | void value_free(value_t); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /ruby/knight/lib/boolean2.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require_relative 'value' 3 | 4 | # The Boolean type in Knight. 5 | module Kn 6 | refine TrueClass do 7 | class Boolean < Value 8 | # Parse a number from the given stream 9 | def self.parse(stream) 10 | stream.slice! /\A([TF])[A-Z]*/ and new($1 == 'T') 11 | end 12 | 13 | # The instance of `Boolean` that represents truthiness. 14 | TRUE = new # note that we call `new` before we redefine it later. 15 | TRUE.instance_exec do 16 | def to_i = 1 17 | def to_s = "true" 18 | def truthy? = true 19 | freeze 20 | end 21 | 22 | # The instance of `Boolean` that represents falsehood. 23 | FALSE = new 24 | FALSE.instance_exec do 25 | def to_i = 0 26 | def to_s = "false" 27 | def truthy? = false 28 | freeze 29 | end 30 | 31 | # Creates a new `Boolean` from the given data, which should be true or false. 32 | # 33 | # This technically doesn't create a new instance, but rather returns `TRUE` or `FALSE`. 34 | def self.new(data) = data ? TRUE : FALSE 35 | 36 | # Booleans are only equal to other instances of themselves. 37 | alias == equal? 38 | 39 | # Compares the truthiness of `self` with the truthiness of `rhs`. 40 | def <=>(rhs) 41 | to_i <=> Boolean.new(rhs.truthy?).to_i 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /c/c-old/src/shared.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_SHARED_H 2 | #define KN_SHARED_H 3 | 4 | #include /* size_t */ 5 | 6 | /* 7 | * A function that's used to halt the execution of the program, writing the 8 | * given message to stderr before exiting with code 1. 9 | * 10 | * Since this aborts the program, it's marked both `noreturn` and `cold`. 11 | */ 12 | void die(const char *msg, ...) __attribute__((noreturn,cold)); 13 | 14 | /* 15 | * A macro that's used to indicate an internal bug occurred. This macro 16 | * _should_ never be executed, as doing so indicates a bug in the program. 17 | */ 18 | #define bug(msg, ...) \ 19 | die("%s:%s:%d: bug encountered: " msg "\n", \ 20 | __FILE__, __func__, __LINE__, __VA_ARGS__) 21 | 22 | /* 23 | * Allocates `size_t` bytes of memory and returns a pointer to it. 24 | * 25 | * This is identical to the stdlib's `malloc`, except the program is aborted 26 | * instead of returning `NULL`. 27 | */ 28 | void *xmalloc(size_t size); 29 | 30 | /* 31 | * Resizes the pointer to a segment of at least `size_t` bytes of memory and, 32 | * returning the new segment's pointer. 33 | * 34 | * This is identical to the stdlib's `realloc`, except the program is aborted 35 | * instead of returning `NULL`. 36 | */ 37 | void *xrealloc(void *ptr, size_t size); 38 | 39 | #endif /* KN_SHARED_H */ 40 | -------------------------------------------------------------------------------- /c/vm/src/bytecode.h: -------------------------------------------------------------------------------- 1 | #ifndef BYTECODE_H 2 | #define BYTECODE_H 3 | 4 | #include "value.h" 5 | 6 | #define MAX_ARGC 4 7 | #define OPCODE_ARGC(arg) ((arg) / 0x20) 8 | 9 | typedef enum { 10 | OP_UNDEFINED = 0, 11 | OP_PROMPT = 0x01, 12 | OP_RANDOM, 13 | OP_PUSHL, // technically has one argc, but we dont want to pop it 14 | 15 | OP_EVAL = 0x20, 16 | // OP_BLOCK, <-- doesn't exist, it's a PUSHL 17 | OP_CALL, 18 | OP_SYSTEM, 19 | OP_QUIT, 20 | OP_NOT, 21 | OP_LENGTH, 22 | OP_DUMP, 23 | OP_OUTPUT, 24 | OP_POP, 25 | 26 | OP_ADD = 0x40, 27 | OP_SUB, 28 | OP_MUL, 29 | OP_DIV, 30 | OP_MOD, 31 | OP_POW, 32 | OP_EQL, 33 | OP_LTH, 34 | OP_GTH, 35 | OP_AND, 36 | OP_OR, 37 | OP_THEN, 38 | OP_ASSIGN, 39 | OP_WHILE, 40 | 41 | OP_IF = 0x60, 42 | OP_GET, 43 | 44 | OP_SET = 0x80, 45 | } opcode_t; 46 | 47 | typedef union { 48 | opcode_t opcode; 49 | value_t value; 50 | } bytecode_t; 51 | 52 | typedef struct { 53 | unsigned length; 54 | union { unsigned capacity, rc; }; // capacity when parsing 55 | bytecode_t *code; 56 | } block_t; 57 | 58 | typedef struct _blockptr_t { 59 | block_t *block; 60 | unsigned ip, rc; 61 | } blockptr_t; 62 | 63 | block_t *block_parse(const char **); 64 | void block_free(block_t *); 65 | void block_clone(block_t *); 66 | void block_dump(const block_t *); 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /c/ast-ext/src/shared.c: -------------------------------------------------------------------------------- 1 | #include /* vfprintf, fprintf */ 2 | #include /* va_list, va_start, va_end */ 3 | #include /* exit, malloc, realloc */ 4 | #include /* assert */ 5 | #include "shared.h" /* prototypes, size_t, ssize_t */ 6 | 7 | void die(const char *fmt, ...) { 8 | va_list args; 9 | 10 | va_start(args, fmt); 11 | vfprintf(stderr, fmt, args); 12 | va_end(args); 13 | 14 | fprintf(stderr, "\n"); 15 | 16 | exit(1); 17 | } 18 | 19 | unsigned long kn_hash(const char *str) { 20 | unsigned long hash; 21 | 22 | assert(str != NULL); 23 | 24 | // This is the MurmurHash. 25 | hash = 525201411107845655; 26 | 27 | while (*str != '\0') { 28 | hash ^= *str++; 29 | hash *= 0x5bd1e9955bd1e995; 30 | hash ^= hash >> 47; 31 | } 32 | 33 | return hash; 34 | } 35 | 36 | void *xmalloc(size_t size) { 37 | assert(0 <= (ssize_t) size); 38 | 39 | void *ptr = malloc(size); 40 | 41 | #ifndef KN_RECKLESS 42 | if (ptr == NULL) 43 | die("malloc failure for size %zd", size); 44 | #endif /* !KN_RECKLESS */ 45 | 46 | return ptr; 47 | } 48 | 49 | void *xrealloc(void *ptr, size_t size) { 50 | assert(0 <= (ssize_t) size); 51 | 52 | ptr = realloc(ptr, size); 53 | 54 | #ifndef KN_RECKLESS 55 | if (ptr == NULL) 56 | die("realloc failure for size %zd", size); 57 | #endif /* !KN_RECKLESS */ 58 | 59 | return ptr; 60 | } 61 | -------------------------------------------------------------------------------- /c/ast/src/shared.c: -------------------------------------------------------------------------------- 1 | #include /* vfprintf, fprintf, stderr */ 2 | #include /* va_list, va_start, va_end */ 3 | #include /* exit, malloc, realloc */ 4 | #include /* assert */ 5 | #include "shared.h" /* prototypes, size_t, ssize_t, NULL */ 6 | 7 | void die(const char *fmt, ...) { 8 | va_list args; 9 | 10 | va_start(args, fmt); 11 | vfprintf(stderr, fmt, args); 12 | va_end(args); 13 | 14 | fprintf(stderr, "\n"); 15 | 16 | exit(1); 17 | } 18 | 19 | unsigned long kn_hash(const char *str) { 20 | unsigned long hash; 21 | 22 | assert(str != NULL); 23 | 24 | // This is the MurmurHash. 25 | hash = 525201411107845655; 26 | 27 | while (*str != '\0') { 28 | hash ^= *str++; 29 | hash *= 0x5bd1e9955bd1e995; 30 | hash ^= hash >> 47; 31 | } 32 | 33 | return hash; 34 | } 35 | 36 | void *xmalloc(size_t size) { 37 | assert(0 <= (ssize_t) size); 38 | 39 | void *ptr = malloc(size); 40 | 41 | #ifndef KN_RECKLESS 42 | if (ptr == NULL) 43 | die("malloc failure for size %zd", size); 44 | #endif /* !KN_RECKLESS */ 45 | 46 | return ptr; 47 | } 48 | 49 | void *xrealloc(void *ptr, size_t size) { 50 | assert(0 <= (ssize_t) size); 51 | 52 | ptr = realloc(ptr, size); 53 | 54 | #ifndef KN_RECKLESS 55 | if (ptr == NULL) 56 | die("realloc failure for size %zd", size); 57 | #endif /* !KN_RECKLESS */ 58 | 59 | return ptr; 60 | } 61 | -------------------------------------------------------------------------------- /csharp/Stream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Knight 5 | { 6 | public class Stream 7 | { 8 | public string Source { get; private set; } 9 | public delegate bool Condition(char c); 10 | 11 | public Stream(string source) => Source = source; 12 | 13 | public bool IsEmpty() => Source == ""; 14 | 15 | public bool StartsWith(Condition cond) => !IsEmpty() && cond(Source[0]); 16 | public bool StartsWith(params char[] chars) => StartsWith(chars.Contains); 17 | 18 | public void StripKeyword() => TakeWhile(c => char.IsUpper(c) || c == '_'); 19 | 20 | public string TakeWhileIfStartsWith(char chr, Condition body) => TakeWhileIfStartsWith(c => c == chr, body); 21 | public string TakeWhileIfStartsWith(char[] chars, Condition body) => TakeWhileIfStartsWith(chars.Contains, body); 22 | public string TakeWhileIfStartsWith(Condition initial, Condition body = null) { 23 | if (!initial(Source[0])) { 24 | return null; 25 | } else { 26 | return Take() + (TakeWhile(body ?? initial) ?? ""); 27 | } 28 | } 29 | 30 | public char Take() { 31 | char c = Source[0]; 32 | Source = Source.Substring(1); 33 | return c; 34 | } 35 | 36 | public string TakeWhile(Condition condition) { 37 | var ret = ""; 38 | 39 | while (!IsEmpty() && condition(Source[0])) { 40 | ret += Take(); 41 | } 42 | 43 | return ret == "" ? null : ret; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /perl/lib/Kn/String.pm: -------------------------------------------------------------------------------- 1 | package Kn::String; 2 | use strict; 3 | use warnings; 4 | 5 | use parent 'Kn::Value'; 6 | 7 | use overload 8 | 'bool' => sub { ${shift->run()} ne '' }, 9 | '0+' => sub { 10 | no warnings; 11 | 12 | ${shift()} =~ m/^\s*[-+]?\d*/p; 13 | 14 | int ${^MATCH}; 15 | }; 16 | 17 | # Converts both arguments to a string and concatenates them. 18 | sub add { 19 | Kn::String->new(shift . shift); 20 | } 21 | 22 | # Duplicates the first argument by the second argument's amount. 23 | sub mul { 24 | Kn::String->new(shift() x shift); 25 | } 26 | 27 | # Compares the two strings lexicographically. 28 | sub cmp { 29 | "$_[0]" cmp "$_[1]" 30 | } 31 | 32 | # Checks to see if two strings are equal. This differs from `Value`'s in that 33 | # we check for equality with `eq` not `==`. 34 | sub eql { 35 | my ($lhs, $rhs) = @_; 36 | 37 | ref($lhs) eq ref($rhs) && $$lhs eq $$rhs 38 | } 39 | 40 | # Parses a string out, which should start with either `'` or `"`, after which 41 | # all characters (except for that quote) are taken literally. If the closing 42 | # quote isn't found, the program fails. 43 | sub parse { 44 | my ($class, $stream) = @_; 45 | 46 | $$stream =~ s/\A(["'])((?:(?!\1).)*)(\1)?//s or return; 47 | die "missing closing quote" unless $3; 48 | 49 | $class->new($2) 50 | } 51 | 52 | # Dumps the class's info. Used for debugging. 53 | sub dump { 54 | "String(${shift()})"; 55 | } 56 | 57 | 1; 58 | -------------------------------------------------------------------------------- /c/c-old/src/string.c: -------------------------------------------------------------------------------- 1 | #include /* assert */ 2 | #include /* free */ 3 | 4 | #include "shared.h" /* xmalloc */ 5 | #include "string.h" /* prototypes, kn_string_t */ 6 | 7 | // not technically "interning" a string, but rather is used for string literals. 8 | struct kn_string_t kn_string_intern(const char *str) { 9 | assert(str != NULL); 10 | 11 | return (struct kn_string_t) { 12 | .str = str, 13 | .rc = NULL 14 | }; 15 | } 16 | 17 | struct kn_string_t kn_string_new(const char *str) { 18 | assert(str != NULL); 19 | 20 | unsigned *rc = xmalloc(sizeof(unsigned)); 21 | *rc = 1; 22 | 23 | return (struct kn_string_t) { 24 | .str = str, 25 | .rc = rc 26 | }; 27 | } 28 | 29 | struct kn_string_t kn_string_clone(const struct kn_string_t *string) { 30 | assert(string != NULL); 31 | 32 | // We accept a const pointer because `kn_string_clone` should obviously 33 | // be callable on a const pointer. However, we have interior mutability, 34 | // so we cast away the constness inside. 35 | if (string->rc != NULL) { 36 | ++*((struct kn_string_t *) string)->rc; 37 | } 38 | 39 | return *string; 40 | } 41 | 42 | void kn_string_free(struct kn_string_t *string) { 43 | assert(string != NULL); 44 | 45 | // We own the string now, so we're free to remove its constness. 46 | if (string->rc != NULL && --(*(string->rc)) == 0) { 47 | free((char *) string->str); 48 | free(string->rc); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /python/knight/stream.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import Union 3 | 4 | class Stream(): 5 | """ The class used when parsing data. """ 6 | WHITESPACE: re.Pattern = re.compile(r'([\s(){}\[\]:]+|\#[^\n]*)+') 7 | 8 | def __init__(self, source: str): 9 | """ Creates a new `Stream` with the given source. """ 10 | self.source = source 11 | 12 | def __str__(self) -> str: 13 | """ Returns the remainder of the stream. """ 14 | return self.source 15 | 16 | def __bool__(self) -> bool: 17 | """ Returns whether the stream is empty. """ 18 | return bool(self.source) 19 | 20 | def strip(self): 21 | """ Removes all leading whitespace and quotes """ 22 | self.matches(Stream.WHITESPACE) 23 | 24 | def peek(self) -> Union[None, str]: 25 | """ Returns the first character of the stream """ 26 | return self.source[0] if self.source else None 27 | 28 | def matches(self, rxp: re.Pattern, index: int = 0) -> Union[None, str]: 29 | """ 30 | Checks to see if the start of the stream matches `rxp`. 31 | 32 | If the stream doesn't match, `None` is returned. Otherwise, the 33 | stream is updated, and the `index`th group is returned. (The 34 | default value of `0` means the entire matched regex is returned.) 35 | """ 36 | match = rxp.match(self.source) 37 | 38 | if match is None: 39 | return None 40 | 41 | string = self.source[:match.end()] 42 | self.source = self.source[match.end():] 43 | return match[index] 44 | -------------------------------------------------------------------------------- /perl/lib/Kn/Ast.pm: -------------------------------------------------------------------------------- 1 | package Kn::Ast; 2 | 3 | use strict; 4 | use warnings; 5 | no warnings 'recursion'; 6 | 7 | use parent 'Kn::Value'; 8 | 9 | use Kn::Function; 10 | 11 | use overload 12 | '0+' => 'run', 13 | '""' => 'run', 14 | 'bool' => 'run'; 15 | 16 | # Creates a new `Ast` with the given function, operator name, and arguments. 17 | # 18 | # While not strictly necessary, the operator name is used for debugging. 19 | sub new { 20 | my ($class, $func, $op, @args) = @_; 21 | 22 | bless { func => $func, op => $op, args => \@args }, $class; 23 | } 24 | 25 | # An Ast is only equivalent to itself. 26 | sub eql { 27 | use Scalar::Util 'refaddr'; 28 | my ($lhs, $rhs) = @_; 29 | 30 | ref($lhs) eq ref($rhs) && refaddr($lhs) == refaddr($rhs); 31 | } 32 | 33 | 34 | sub run { 35 | $_[0]->{func}->run(@{$_[0]->{args}}) 36 | } 37 | 38 | sub parse { 39 | my ($class, $stream) = @_; 40 | 41 | $$stream =~ s$\A[A-Z]+|\A[-+*/%^&|!`;=]$$p or return; 42 | my $op = substr ${^MATCH}, 0, 1; 43 | my $func = Kn::Function->get($op) or die "unknown function '$op'"; 44 | 45 | my $ret = $class->new($func, $op, 46 | map { Kn::Value->parse($stream) } (1..$func->arity()) 47 | ); 48 | $ret 49 | } 50 | 51 | # Dumps the class's info. Used for debugging. 52 | sub dump { 53 | my $this = shift; 54 | my $ret = "Function($this->{op}"; 55 | 56 | for my $x(@{$this->{args}}) { 57 | $ret .= ', ' . $x->dump(); 58 | }; 59 | 60 | "$ret)"; 61 | } 62 | 63 | 1; 64 | -------------------------------------------------------------------------------- /python/knight/literal.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from knight import Value 3 | from typing import TypeVar, Generic 4 | 5 | T = TypeVar('T') 6 | 7 | class Literal(Value, Generic[T], parse=False): 8 | """ 9 | A class used to represent a value that has a piece of data associated 10 | with it. 11 | 12 | Thi sis not meant to be initialized directly, and instead the 13 | subclasses of it should be used. 14 | """ 15 | 16 | data: T 17 | 18 | def __init__(self, data: T): 19 | """ Creates a new `Literal` instance with the given data. """ 20 | super().__init__() 21 | self.data = data 22 | 23 | def run(self) -> Value: 24 | """ Running a Literal simply returns itself. """ 25 | return self 26 | 27 | def __str__(self) -> str: 28 | """ Simply converts this class's `data` to a `str`. """ 29 | return str(self.data) 30 | 31 | def __int__(self) -> int: 32 | """ Simply converts this class's `data` to an `int`. """ 33 | return int(self.data) 34 | 35 | def __bool__(self) -> bool: 36 | """ Simply converts this class's `data` to an `bool`. """ 37 | return bool(self.data) 38 | 39 | def __repr__(self) -> str: 40 | """ Gets a debugging representation of this class. """ 41 | return f'{type(self).__name__}({self})' 42 | 43 | def __eq__(self, rhs: Value) -> bool: 44 | """ 45 | Returns whether `rhs` is of the _same_ class, 46 | and their data is equivalent. 47 | """ 48 | return type(self) == type(rhs) and self.data == rhs.data 49 | -------------------------------------------------------------------------------- /ruby/knight.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative 'boolean' 3 | require_relative 'identifier' 4 | require_relative 'number' 5 | require_relative 'null' 6 | require_relative 'string' 7 | require_relative 'function' 8 | 9 | class Object 10 | alias run itself 11 | end 12 | 13 | module Kn 14 | module_function 15 | 16 | def run(stream) 17 | parse(stream.to_s.dup).run 18 | end 19 | 20 | def parse(stream) 21 | # strip leading comments and whitespace 22 | stream.slice! /\A(?:[\s(){}\[\]:]+|\#[^\n]*)+/m 23 | 24 | # parse out the value 25 | case 26 | when stream.slice!(/\A[a-z_][a-z0-9_]*/) then Identifier.new $& 27 | when stream.slice!(/\A\d+/) then Number.new $&.to_i 28 | when stream.slice!(/\A([TF])[A-Z_]*/) then Boolean.new $1 == 'T' 29 | when stream.slice!(/\AN[A-Z_]*/) then Null.new 30 | when stream.slice!(/\A(?:'([^']*)'|"([^"]*)")/m) then String.new $+ 31 | when (func = Function[name = stream[0]]) 32 | stream.slice! /\A([A-Z_]+|.)/ 33 | Function.new func, name, func.arity.times.map { parse stream } 34 | else 35 | raise ParseError, "unknown token start '#{stream[0]}'" 36 | end 37 | end 38 | end 39 | 40 | 41 | if __FILE__ == $0 42 | def usage = abort("usage: #{File.basename $0} (-e 'program' | -f file)") 43 | 44 | case ($*.length == 2 && $*.shift) 45 | when '-e' then Kn.run $*.shift 46 | when '-f' then Kn.run open($*.shift, &:read) 47 | else usage 48 | end 49 | end -------------------------------------------------------------------------------- /rust/src/main.rs: -------------------------------------------------------------------------------- 1 | use knightrs::{RuntimeError, Environment}; 2 | use clap::{App, Arg, ArgMatches}; 3 | 4 | fn run(matches: ArgMatches<'_>) -> Result<(), RuntimeError> { 5 | let mut env = Environment::default(); 6 | 7 | if let Some(expr) = matches.value_of("expr") { 8 | knightrs::run_str(&expr, &mut env)?; 9 | } else if let Some(filename) = matches.value_of("file") { 10 | knightrs::run_str(std::fs::read_to_string(filename)?, &mut env)?; 11 | } else { 12 | eprintln!("{}", matches.usage()); 13 | std::process::exit(1); 14 | } 15 | 16 | Ok(()) 17 | } 18 | 19 | fn main() { 20 | let matches = 21 | App::new("knight") 22 | .version(clap::crate_version!()) 23 | .author(clap::crate_authors!()) 24 | .about("The Knight programming language") 25 | .usage("knight (-e 'expr' | -f file)") 26 | .arg(Arg::with_name("expr") 27 | .help("the expression to execute") 28 | .takes_value(true) 29 | .conflicts_with("file") 30 | .short("e") 31 | .long("expr")) 32 | .arg(Arg::with_name("file") 33 | .help("the expression to read and execute") 34 | .takes_value(true) 35 | .conflicts_with("expr") 36 | .short("f") 37 | .long("file")) 38 | // .get_matches_from(vec!["--", "-f", "../knight.kn"]); 39 | .get_matches(); 40 | 41 | match run(matches) { 42 | Err(RuntimeError::Quit(code)) => std::process::exit(code), 43 | Err(err) => { 44 | eprintln!("error: {}", err); 45 | std::process::exit(1) 46 | }, 47 | Ok(()) => { /* do nothing */ } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /csharp/Number.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Knight.Ops; 3 | namespace Knight 4 | { 5 | public class Number : Literal, IAdd, ISub, IMul, IDiv, IMod, IPow, IComparable 6 | { 7 | public Number(long data) : base(data) {} 8 | 9 | public static Number Parse(Stream stream) { 10 | var contents = stream.TakeWhileIfStartsWith(char.IsDigit); 11 | 12 | return contents == null ? null : new Number(long.Parse(contents)); 13 | } 14 | 15 | public override void Dump() => Console.Write($"Number({this})"); 16 | 17 | public override bool ToBoolean() => _data != 0; 18 | public override long ToNumber() => _data; 19 | 20 | public int CompareTo(IValue other) => _data.CompareTo(other.ToNumber()); 21 | 22 | public IValue Add(IValue rhs) => new Number(_data + rhs.ToNumber()); 23 | public IValue Sub(IValue rhs) => new Number(_data - rhs.ToNumber()); 24 | public IValue Mul(IValue rhs) => new Number(_data * rhs.ToNumber()); 25 | public IValue Div(IValue rhs) { 26 | var rlong = rhs.ToNumber(); 27 | 28 | if (rlong == 0) { 29 | throw new RuntimeException("Cannot divide by zero!"); 30 | } 31 | 32 | return new Number(_data / rlong); 33 | } 34 | 35 | public IValue Mod(IValue rhs) { 36 | var rlong = rhs.ToNumber(); 37 | 38 | if (rlong == 0) { 39 | throw new RuntimeException("Cannot modulo by zero!"); 40 | } 41 | 42 | return new Number(_data % rlong); 43 | } 44 | 45 | public IValue Pow(IValue rhs) => new Number((long) Math.Pow(_data, rhs.ToNumber())); 46 | } 47 | } -------------------------------------------------------------------------------- /c/ast-ext/src/shared.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_SHARED_H 2 | #define KN_SHARED_H 3 | 4 | #include /* size_t */ 5 | 6 | /* 7 | * A function that's used to halt the execution of the program, writing the 8 | * given message to stderr before exiting with code 1. 9 | * 10 | * Since this aborts the program, it's marked both `noreturn` and `cold`. 11 | */ 12 | void die(const char *msg, ...) __attribute__((noreturn,cold)); 13 | 14 | #ifdef KN_RECKLESS 15 | # define assert_reckless(_) \ 16 | do { } while(0) 17 | #else 18 | # define assert_reckless(value) \ 19 | do { if (!(value)) die("invalid value: %s", #value); } while(0) 20 | #endif /* KN_RECKLESS */ 21 | 22 | /* 23 | * Returns a hash for the given string. 24 | */ 25 | unsigned long kn_hash(const char *str); 26 | 27 | /* 28 | * Allocates `size_t` bytes of memory and returns a pointer to it. 29 | * 30 | * This is identical to the stdlib's `malloc`, except the program is aborted 31 | * instead of returning `NULL`. 32 | * 33 | * The `size`, when converted to an `ssize_t`, must be nonnegative. 34 | */ 35 | void *xmalloc(size_t size) __attribute__((malloc)); 36 | 37 | /* 38 | * Resizes the pointer to a segment of at least `size_t` bytes of memory and, 39 | * returning the new segment's pointer. 40 | * 41 | * This is identical to the stdlib's `realloc`, except the program is aborted 42 | * instead of returning `NULL`. 43 | * 44 | * The `size`, when converted to an `ssize_t`, must be nonnegative. 45 | */ 46 | void *xrealloc(void *ptr, size_t size); 47 | 48 | #endif /* KN_SHARED_H */ 49 | -------------------------------------------------------------------------------- /c/ast/src/shared.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_SHARED_H 2 | #define KN_SHARED_H 3 | 4 | #include /* size_t */ 5 | 6 | /* 7 | * A function that's used to halt the execution of the program, writing the 8 | * given message to stderr before exiting with code 1. 9 | * 10 | * Since this aborts the program, it's marked both `noreturn` and `cold`. 11 | */ 12 | void die(const char *msg, ...) __attribute__((noreturn,cold)); 13 | 14 | #ifdef KN_RECKLESS 15 | # define assert_reckless(_) do { } while(0) 16 | #else 17 | # define assert_reckless(value) \ 18 | do { \ 19 | if (!(value)) die("invalid value: %s", #value); \ 20 | } while(0) 21 | #endif /* KN_RECKLESS */ 22 | 23 | /* 24 | * Returns a hash for the given string. 25 | */ 26 | unsigned long kn_hash(const char *str); 27 | 28 | /* 29 | * Allocates `size_t` bytes of memory and returns a pointer to it. 30 | * 31 | * This is identical to the stdlib's `malloc`, except the program is aborted 32 | * instead of returning `NULL`. 33 | * 34 | * The `size`, when converted to an `ssize_t`, must be nonnegative. 35 | */ 36 | void *xmalloc(size_t size) __attribute__((malloc)); 37 | 38 | /* 39 | * Resizes the pointer to a segment of at least `size_t` bytes of memory and, 40 | * returning the new segment's pointer. 41 | * 42 | * This is identical to the stdlib's `realloc`, except the program is aborted 43 | * instead of returning `NULL`. 44 | * 45 | * The `size`, when converted to an `ssize_t`, must be nonnegative. 46 | */ 47 | void *xrealloc(void *ptr, size_t size); 48 | 49 | #endif /* KN_SHARED_H */ 50 | -------------------------------------------------------------------------------- /cpp/src/variable.cpp: -------------------------------------------------------------------------------- 1 | #include "variable.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace kn; 7 | 8 | Variable::Variable(std::string name) noexcept : name(name) {} 9 | 10 | // The set of all known variables. 11 | static std::unordered_map> ENVIRONMENT; 12 | 13 | std::optional Variable::parse(std::string_view& view) { 14 | char front = view.front(); 15 | 16 | if (!std::islower(front) && front != '_') 17 | return std::nullopt; 18 | 19 | auto start = view.cbegin(); 20 | 21 | do { 22 | view.remove_prefix(1); 23 | front = view.front(); 24 | } while (std::islower(front) || front == '_' || std::isdigit(front)); 25 | 26 | auto identifier = std::string_view(start, view.cbegin() - start); 27 | 28 | if (auto match = ENVIRONMENT.find(identifier); match != ENVIRONMENT.cend()) 29 | return std::make_optional(match->second); 30 | 31 | auto variable = std::make_shared(std::string(identifier)); 32 | auto result = ENVIRONMENT.emplace(std::string_view(variable->name), std::move(variable)); 33 | 34 | return std::make_optional(result.first->second); 35 | } 36 | 37 | std::ostream& Variable::dump(std::ostream& out) const noexcept { 38 | return out << "Variable(" << name << ")"; 39 | } 40 | 41 | Value Variable::run() { 42 | if (!value) 43 | throw Error("unknown variable encountered: " + name); 44 | 45 | return *value; 46 | } 47 | 48 | void Variable::assign(Value newvalue) noexcept { 49 | value = std::move(newvalue); 50 | } 51 | -------------------------------------------------------------------------------- /cpp/src/value.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "error.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace kn { 12 | using number = long long; 13 | using string = std::string; 14 | struct null {}; 15 | 16 | 17 | class Variable; 18 | class Function; 19 | 20 | class Value { 21 | std::variant< 22 | null, 23 | bool, 24 | number, 25 | std::shared_ptr, 26 | std::shared_ptr, 27 | std::shared_ptr 28 | > data; 29 | 30 | public: 31 | 32 | explicit Value() noexcept; 33 | explicit Value(bool boolean) noexcept; 34 | explicit Value(number num) noexcept; 35 | explicit Value(string str) noexcept; 36 | explicit Value(std::shared_ptr str) noexcept; 37 | explicit Value(std::shared_ptr var) noexcept; 38 | explicit Value(std::shared_ptr func) noexcept; 39 | static std::optional parse(std::string_view& view); 40 | 41 | Value run(); 42 | std::ostream& dump(std::ostream& out) const; 43 | 44 | bool to_boolean(); 45 | number to_number(); 46 | std::shared_ptr to_string(); 47 | std::shared_ptr as_variable() const; 48 | 49 | Value operator+(Value&& rhs); 50 | Value operator-(Value&& rhs); 51 | Value operator*(Value&& rhs); 52 | Value operator/(Value&& rhs); 53 | Value operator%(Value&& rhs); 54 | Value pow(Value&& rhs); 55 | 56 | bool operator==(Value&& rhs); 57 | bool operator<(Value&& rhs); 58 | bool operator>(Value&& rhs); 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /python/knight/null.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from knight import Value, Stream, Literal, RunError 3 | from typing import Union 4 | import re 5 | 6 | class Null(Literal[None]): 7 | """ Used to represent the null class. """ 8 | 9 | REGEX = re.compile(r'N[A-Z]*') 10 | 11 | @classmethod 12 | def parse(cls, stream: Stream) -> Union[None, Null]: 13 | """ Parses `Null` if the stream starts with `N`. """ 14 | if stream.matches(Null.REGEX): 15 | return cls() 16 | else: 17 | return None 18 | 19 | def __init__(self): 20 | """ 21 | Creates a new Null. 22 | 23 | Note that this is overloaded because `Literal` expects an argument 24 | for `data`, but `null` should be constructible without specifying 25 | the `data` field, so this does that for us. 26 | """ 27 | super().__init__(None) 28 | 29 | def __int__(self) -> int: 30 | """ Simply returns `0` """ 31 | return 0 32 | 33 | def __str__(self) -> str: 34 | """ Simply returns `"null"` """ 35 | return 'null' 36 | 37 | def __repr__(self) -> str: 38 | """ Gets a debugging representation of this class. """ 39 | return 'Null()' 40 | 41 | def __eq__(self, rhs: Value) -> bool: 42 | """ 43 | Null is only equal to itself, and as we have one `INSTANCE`, we 44 | can use `is`. 45 | """ 46 | return isinstance(rhs, Null) 47 | 48 | def __lt__(self, _: Value): 49 | """ Comparisons to Null are invalid. """ 50 | raise RunError('cannot compare with Null.') 51 | 52 | def __gt__(self, _: Value): 53 | """ Comparisons to Null are invalid. """ 54 | raise RunError('cannot compare with Null.') 55 | -------------------------------------------------------------------------------- /haskell/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Sam Westerman (c) 2020 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Sam Westerman nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /c/c-old/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "knight.h" 6 | #include "shared.h" 7 | 8 | void usage(const char *program_name) { 9 | die("usage: %s [-e program] [-f file]", program_name); 10 | } 11 | 12 | char *read_file(const char *filename) { 13 | FILE *file = fopen(filename, "r"); 14 | 15 | if (file == NULL) { 16 | die("unable to read file '%s': %s", filename, strerror(errno)); 17 | } 18 | 19 | size_t len = 0; 20 | size_t cap = 2048; 21 | char *contents = xmalloc(cap); 22 | 23 | while (!feof(file)) { 24 | size_t nchars = fread(&contents[len], 1, cap - len, file); 25 | 26 | if (nchars == 0) { 27 | if (feof(stdin)) { 28 | break; 29 | } else { 30 | die("unable to line in file '%s': %s'", 31 | filename, strerror(errno)); 32 | } 33 | } 34 | 35 | len += nchars; 36 | 37 | if (len == cap) { 38 | cap *= 2; 39 | contents = xrealloc(contents, cap); 40 | } 41 | } 42 | 43 | fclose(file); 44 | return xrealloc(contents, len); 45 | } 46 | 47 | int main(int argc, const char **argv) { 48 | // note: to keep it cross-platform, i opted not to use optparse. 49 | if (argc != 3) { 50 | usage(argv[0]); 51 | } 52 | 53 | if (strlen(argv[1]) != 2 || argv[1][0] != '-') { 54 | usage(argv[0]); 55 | } 56 | 57 | const char *string; 58 | 59 | switch(argv[1][1]) { 60 | case 'e': 61 | string = argv[2]; 62 | break; 63 | case 'f': 64 | string = read_file(argv[2]); 65 | break; 66 | default: 67 | usage(argv[0]); 68 | } 69 | 70 | kn_init(); 71 | 72 | struct kn_value_t ret = kn_run(string); 73 | 74 | kn_value_free(&ret); 75 | 76 | // kn_free(); 77 | 78 | return 0; 79 | } 80 | -------------------------------------------------------------------------------- /csharp/String.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Knight.Ops; 3 | 4 | namespace Knight 5 | { 6 | public class String : Literal, IAdd, IMul, IComparable 7 | { 8 | public String(string data) : base(data) {} 9 | 10 | public static String Parse(Stream stream) { 11 | if (!stream.StartsWith('\'', '\"')) 12 | return null; 13 | 14 | char quote = stream.Take(); 15 | var start = stream.Source; 16 | 17 | string data = stream.TakeWhile(c => c != quote); 18 | if (stream.IsEmpty()) { 19 | throw new RuntimeException($"unterminated string, starting at {start}"); 20 | } 21 | 22 | stream.Take(); // remove trailling quote. 23 | 24 | return new String(data ?? ""); 25 | } 26 | 27 | public override void Dump() => Console.Write($"String({_data})"); 28 | 29 | public override bool ToBoolean() => _data != ""; 30 | public override long ToNumber() { 31 | long ret = 0; 32 | var str = _data.TrimStart(); 33 | 34 | if (str == "") 35 | return 0; 36 | 37 | var isNegative = str[0] == '-'; 38 | 39 | if (str[0] == '-' || str[0] == '+') 40 | str = str.Substring(1); 41 | 42 | for (; str != "" && char.IsDigit(str[0]); str = str.Substring(1)) 43 | ret = ret * 10 + (str[0] - '0'); 44 | 45 | if (isNegative) 46 | ret *= -1; 47 | 48 | return ret; 49 | } 50 | 51 | public IValue Add( IValue rhs) => new String(_data + rhs); 52 | public IValue Mul(IValue rhs) { 53 | var s = ""; 54 | var amnt = rhs.ToNumber(); 55 | 56 | for (long i = 0; i < amnt; ++i) 57 | s += _data; 58 | return new String(s); 59 | } 60 | 61 | public int CompareTo(IValue obj) => string.Compare(_data, obj.ToString(), StringComparison.Ordinal); 62 | } 63 | } -------------------------------------------------------------------------------- /c/ast-ext/src/ast.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_AST_H 2 | #define KN_AST_H 3 | 4 | #include "function.h" /* kn_function */ 5 | #include "value.h" /* kn_value */ 6 | #include /* bool */ 7 | 8 | #ifdef KN_DYNMAIC_ARGC 9 | # define KN_AST_ARITY(ast) ((ast)->argc) 10 | #else 11 | # define KN_AST_ARITY(ast) ((ast)->func->arity) 12 | #endif /* KN_DYNMAIC_ARGC */ 13 | 14 | /* 15 | * The type that represents a function and its arguments in Knight. 16 | * 17 | * Note that this struct should be passed to `kn_ast_free` to release its 18 | * resources. 19 | */ 20 | struct kn_ast { 21 | /* 22 | * The function associated with this ast. 23 | */ 24 | const struct kn_function *func; 25 | 26 | /* 27 | * How many references to this object exist. 28 | */ 29 | unsigned refcount; 30 | 31 | #ifdef KN_DYNMAIC_ARGC 32 | /* 33 | * One of the extensions allows us to have a dynamic argc (ie collapse 34 | * adjacent calls to the same function). 35 | */ 36 | unsigned argc; 37 | #endif /* KN_DYNMAIC_ARGC */ 38 | 39 | /* 40 | * The arguments to this struct. 41 | */ 42 | kn_value args[]; 43 | }; 44 | 45 | /* 46 | * Allocates a new `kn_ast` with the given number of arguments. 47 | */ 48 | struct kn_ast *kn_ast_alloc(unsigned argc); 49 | 50 | /* 51 | * Duplicates the ast, returning a new value that must be `kn_ast_free`d 52 | * independently from the passed `ast`. 53 | */ 54 | struct kn_ast *kn_ast_clone(struct kn_ast *ast); 55 | 56 | /* 57 | * Releases the memory resources associated with this struct. 58 | */ 59 | void kn_ast_free(struct kn_ast *ast); 60 | 61 | /* 62 | * Executes a `kn_ast`, returning the value associated with its execution. 63 | */ 64 | kn_value kn_ast_run(struct kn_ast *ast); 65 | 66 | #endif /* !KN_AST_H */ 67 | -------------------------------------------------------------------------------- /php/src/Nil.php: -------------------------------------------------------------------------------- 1 | match('N[A-Z]*')) ? null : new self(); 23 | } 24 | 25 | /** 26 | * Converts this `Nil` to a string. 27 | * 28 | * @return string Simply `'null'`. 29 | **/ 30 | public function __toString(): string 31 | { 32 | return 'null'; 33 | } 34 | 35 | /** 36 | * Converts this `Nil` to an int. 37 | * 38 | * @return int Simply `0`. 39 | **/ 40 | public function toInt(): int 41 | { 42 | return 0; 43 | } 44 | 45 | /** 46 | * Converts this `Nil` to an boolean. 47 | * 48 | * @return bool Simply `false`. 49 | **/ 50 | public function toBool(): bool 51 | { 52 | return false; 53 | } 54 | 55 | /** 56 | * Gets a string representation of this class, for debugging purposes. 57 | * 58 | * @return string 59 | **/ 60 | public function dump(): string 61 | { 62 | return "Null()"; 63 | } 64 | 65 | /** 66 | * Checks to see if `$value` is a `Nil` and equal to `$this`. 67 | * 68 | * @return bool 69 | **/ 70 | public function eql(Value $value): bool 71 | { 72 | return is_a($value, get_class()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /raku/lib/Knight/TypedValue.rakumod: -------------------------------------------------------------------------------- 1 | use Knight::Value; 2 | 3 | #| This role is used for literal values (asides from `Null`) within Knight, as they all have an associated Raku type 4 | #| That we're essentially wrapping. 5 | #| 6 | #| - `::T` type should be the builtin type, such as `Bool` or `Str`. 7 | #| - `&cmp` should be a function that takes two `T`s, and returns the first's ordering to the second. 8 | #| - `&eql` should be a function that takes two `T`s, and returns whether they are equal. 9 | unit role Knight::TypedValue[::T, &cmp, &eql] does Knight::Value; 10 | 11 | #| The value that the implementor has 12 | #| 13 | #| For example, Booleans have a `Bool` `$!value`. 14 | has T $!value is built; 15 | 16 | #| Creates a new `self` with the given `$value`. 17 | method new(T $value) { 18 | self.bless :$value 19 | } 20 | 21 | #| Compares `self` with the `$rhs`. 22 | #| 23 | #| This simply calls `$cmp` with our `$!value` and `$rhs` transformed into a `T`. 24 | method cmp(Knight::Value $rhs, --> Order) { 25 | &cmp($!value, T($rhs)) 26 | } 27 | 28 | #| Checks to see if `self` is equal to `$rhs`. 29 | #| 30 | #| According to the Knight specifications, only values that have the same type and values are equal. 31 | multi method eql(::?CLASS $rhs, --> Bool) { 32 | &eql($!value, T($rhs)) 33 | } 34 | 35 | #| Converts `self` to a string by converting `$!value` to a `Str`. 36 | method Str(--> Str) is pure { ~$!value } 37 | 38 | #| Converts `self` to a boolean by converting `$!value` to a `Bool`. 39 | method Bool(--> Bool) is pure { ?$!value } 40 | 41 | #| Converts `self` to an integer by converting `$!value` to an `Int`. 42 | method Int(--> Int) is pure { $!value.Int } 43 | 44 | #| Running a `TypedValue` simply returns `self`. 45 | method run(--> Knight::Value) is pure { self } 46 | -------------------------------------------------------------------------------- /php/src/Stream.php: -------------------------------------------------------------------------------- 1 | source = $source; 24 | } 25 | 26 | /** 27 | * Removes all leading whitespace and comments. 28 | * 29 | * Note that, for Knight, all forms of parens (ie `[`, `]`, `(`, `)`, `{`, and `}`), as well as the colon (`:`) are 30 | * considered whitespace. 31 | * 32 | * @return void 33 | **/ 34 | public function strip(): void 35 | { 36 | $this->source = preg_replace('/\A(?:[\]\[\s(){}:]+|\#[^\n]*(\n|$))*/m', '', $this->source); 37 | } 38 | 39 | /** 40 | * Attempts to match the $regex at the start of the source. 41 | * 42 | * If the regex matches, the entire matching string will be returned by default. The `$idx` parameter can be used to 43 | * change this behaviour around. 44 | * 45 | * @param string $regex The regex to match at the beginning of the string; It will have the `/m` flag applied. 46 | * @param int $idx The index of the group to return; defaults to `0`, ie the entire match. 47 | * @return string|null Returns the matching string (or the `$idx` capture group) if `$regex` matched. Else, null. 48 | **/ 49 | public function match(string $regex, int $idx=0): ?string 50 | { 51 | if (!preg_match("/\A(?:$regex)/m", $this->source, $match)) { 52 | return null; 53 | } 54 | 55 | $this->source = substr($this->source, strlen($match[0])); 56 | 57 | return $match[$idx]; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /python/knight/identifier.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from knight import Value, Stream, RunError 3 | from typing import Union, Dict 4 | import re 5 | 6 | _ENV: Dict[str, Value] = {} 7 | 8 | class Identifier(Value): 9 | """ 10 | Represents an identifier within Knight. 11 | 12 | Because all identifiers in Knight are global---and don't go out of 13 | scope---we have a single dict that keeps track of _all_ identifiers. 14 | """ 15 | 16 | REGEX: re.Pattern = re.compile(r'[a-z_][a-z0-9_]*') 17 | name: str 18 | 19 | @classmethod 20 | def parse(cls, stream: Stream) -> Union[None, Identifier]: 21 | """ 22 | Parses an Identifier out from the stream. 23 | 24 | This returns `None` if the stream doesn't start with a lowercase 25 | letter, or an underscore. 26 | """ 27 | if match := stream.matches(Identifier.REGEX): 28 | return cls(match) 29 | else: 30 | return None 31 | 32 | def __init__(self, name: str): 33 | """ Creates a new Identifier associated with the given `name`. """ 34 | self.name = name 35 | 36 | def __repr__(self) -> str: 37 | """ Gets a debugging mode representation of this identifier. """ 38 | return f"Identifier({self.name})" 39 | 40 | def run(self) -> Value: 41 | """ 42 | Fetches the value associated with this identifier from the list 43 | of known identifiers. 44 | 45 | If the identifier has not been assigned yet (cf `assign`), then a 46 | `RunError` will be raised. 47 | """ 48 | if self.name in _ENV: 49 | return _ENV[self.name] 50 | else: 51 | raise RunError(f"unknown identifier '{self.name}'") 52 | 53 | 54 | def assign(self, value: Value): 55 | """ 56 | Associated the Value `value` with this identifier. 57 | 58 | Any previously associated value with this identifier is discarded. 59 | """ 60 | _ENV[self.name] = value 61 | -------------------------------------------------------------------------------- /csharp/Knight.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | 5 | namespace Knight 6 | { 7 | public class Kn 8 | { 9 | 10 | internal static IValue Parse(Stream stream) { 11 | while (!stream.IsEmpty()) { 12 | // strip comments. 13 | if (stream.TakeWhileIfStartsWith('#', c => c != '\n') != null) 14 | continue; 15 | 16 | // strip whitespace. 17 | if (stream.TakeWhile(c => char.IsWhiteSpace(c) || "(){}[]:".Contains(c)) != null) 18 | continue; 19 | 20 | // if we neither had comments or whitespace, break out. 21 | break; 22 | } 23 | 24 | if (stream.IsEmpty()) 25 | return null; 26 | 27 | return Number.Parse(stream) ?? 28 | Boolean.Parse(stream) ?? 29 | String.Parse(stream) ?? 30 | Null.Parse(stream) ?? 31 | (IValue) Identifier.Parse(stream) ?? 32 | Function.Parse(stream); 33 | } 34 | 35 | public static IValue Run(string stream) => Run(new Stream(stream)); 36 | public static IValue Run(Stream stream) { 37 | if (stream.IsEmpty()) { 38 | throw new ParseException("nothing to parse."); 39 | } 40 | 41 | IValue value = Parse(stream); 42 | 43 | if (value == null) { 44 | throw new ParseException($"Unknown token start '{stream.Take()}'."); 45 | } else { 46 | return value.Run(); 47 | } 48 | } 49 | 50 | static int Main(string[] args) { 51 | if (args.Length != 2 || args[0] != "-e" && args[0] != "-f") { 52 | Console.Error.WriteLine("usage: {0} (-e 'program' | -f file)", Environment.GetCommandLineArgs()[0]); 53 | return 1; 54 | } 55 | 56 | try { 57 | Run(args[0] == "-e" ? args[1] : File.ReadAllText(args[1])); 58 | return 0; 59 | } catch (KnightException err) { 60 | Console.Error.WriteLine("invalid program: {0}", err.Message); 61 | return 1; 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /asm/debug.s: -------------------------------------------------------------------------------- 1 | .ifndef KN_DEBUG 2 | .equ KN_DEBUG, 1 3 | .endif // .ifndef KN_DEBUG 4 | 5 | .macro todo msg:req 6 | lea kn_debug_todo_\@(%rip), %rdi 7 | // ensure that the stack is properly aligned 8 | mov %rsp, %rcx 9 | mov %esp, %eax 10 | sub $8, %rcx 11 | and $8, %eax 12 | cmp $0, %eax 13 | cmovnz %rcx, %rsp 14 | jmp abort 15 | .pushsection .data, "" 16 | kn_debug_todo_\@: 17 | .asciz "todo: \msg\n" 18 | .popsection 19 | .endm 20 | 21 | .ifdef KN_DEBUG 22 | .macro assert_eq lhs:req rhs:req 23 | cmp \lhs, \rhs 24 | je assert_eq_\@ 25 | // We just assume (for now) that lhs and rhs arent equal to rsi or rdi 26 | mov \lhs, %rsi 27 | mov \rhs, %rdi 28 | lea assertion_failed(%rip), %rdi 29 | jmp kn_debug_write_abort 30 | assert_eq_\@: 31 | .endm // .macro assert_eq 32 | 33 | .macro assert_test lhs:req rhs:req 34 | test \lhs, \rhs 35 | jnz assert_test_\@ 36 | // We just assume (for now) that lhs and rhs arent equal to rsi or rdi 37 | mov \lhs, %rsi 38 | mov \rhs, %rdi 39 | lea assertion_failed(%rip), %rdi 40 | jmp kn_debug_write_abort 41 | assert_test_\@: 42 | .endm // .macro assert_test 43 | 44 | 45 | // Note that this is not a proper function, it takes arguments 46 | // at `rsi` and `4dx`. 47 | kn_debug_write_abort: 48 | // ensure that the stack is properly aligned 49 | mov %rsp, %rcx 50 | mov %esp, %eax 51 | sub $8, %rcx 52 | and $8, %eax 53 | cmp $0, %eax 54 | cmovnz %rcx, %rsp 55 | jmp abort 56 | 57 | .pushsection .data, "" 58 | assertion_failed: 59 | .asciz "assertion failed\n" // : %1$ld (0x%1$lx) != %2$ld (0x%2$lx)\n 60 | .popsection 61 | .else 62 | .macro assert_eq cond:req val:req 63 | .endm // .macro assert_eq 64 | .macro assert_test cond:req val:req 65 | .endm // .macro assert_test 66 | .endif // .ifdef KN_DEBUG 67 | 68 | .macro assert_z reg:req 69 | assert_eq $0, \reg 70 | .endm // assert_z 71 | -------------------------------------------------------------------------------- /asm/old/debug.s: -------------------------------------------------------------------------------- 1 | .ifndef KN_DEBUG 2 | .equ KN_DEBUG, 1 3 | .endif // .ifndef KN_DEBUG 4 | 5 | .macro todo msg:req 6 | lea kn_debug_todo_\@(%rip), %rdi 7 | // ensure that the stack is properly aligned 8 | mov %rsp, %rcx 9 | mov %esp, %eax 10 | sub $8, %rcx 11 | and $8, %eax 12 | cmp $0, %eax 13 | cmovnz %rcx, %rsp 14 | jmp abort 15 | .pushsection .data, "" 16 | kn_debug_todo_\@: 17 | .asciz "todo: \msg\n" 18 | .popsection 19 | .endm 20 | 21 | .ifdef KN_DEBUG 22 | .macro assert_eq lhs:req rhs:req 23 | cmp \lhs, \rhs 24 | je assert_eq_\@ 25 | // We just assume (for now) that lhs and rhs arent equal to rsi or rdi 26 | mov \lhs, %rsi 27 | mov \rhs, %rdi 28 | lea assertion_failed(%rip), %rdi 29 | jmp kn_debug_write_abort 30 | assert_eq_\@: 31 | .endm // .macro assert_eq 32 | 33 | .macro assert_test lhs:req rhs:req 34 | test \lhs, \rhs 35 | jnz assert_test_\@ 36 | // We just assume (for now) that lhs and rhs arent equal to rsi or rdi 37 | mov \lhs, %rsi 38 | mov \rhs, %rdi 39 | lea assertion_failed(%rip), %rdi 40 | jmp kn_debug_write_abort 41 | assert_test_\@: 42 | .endm // .macro assert_test 43 | 44 | 45 | // Note that this is not a proper function, it takes arguments 46 | // at `rsi` and `4dx`. 47 | kn_debug_write_abort: 48 | // ensure that the stack is properly aligned 49 | mov %rsp, %rcx 50 | mov %esp, %eax 51 | sub $8, %rcx 52 | and $8, %eax 53 | cmp $0, %eax 54 | cmovnz %rcx, %rsp 55 | jmp abort 56 | 57 | .pushsection .data, "" 58 | assertion_failed: 59 | .asciz "assertion failed\n" // : %1$ld (0x%1$lx) != %2$ld (0x%2$lx)\n 60 | .popsection 61 | .else 62 | .macro assert_eq cond:req val:req 63 | .endm // .macro assert_eq 64 | .macro assert_test cond:req val:req 65 | .endm // .macro assert_test 66 | .endif // .ifdef KN_DEBUG 67 | 68 | .macro assert_z reg:req 69 | assert_eq $0, \reg 70 | .endm // assert_z 71 | -------------------------------------------------------------------------------- /cpp/src/function.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "value.hpp" 4 | #include 5 | 6 | namespace kn { 7 | // The argument type that functions must accept. 8 | using args_t = std::vector; 9 | 10 | // The pointer type that all functions must fulfill. 11 | using funcptr_t = Value(*)(args_t&); 12 | 13 | // The class that represents a function and its arguments within Knight. 14 | class Function { 15 | // A pointer to the function associated with this class. 16 | funcptr_t const func; 17 | 18 | // The name of the function; used only within `DUMP`. 19 | char const name; 20 | 21 | // The unevaluated arguments associated with this function. 22 | args_t args; 23 | 24 | // Creates a function with the given function and arguments. 25 | // 26 | // This is private because the only way to create a `Function` is through `parse`. 27 | Function(funcptr_t func, char name, args_t args); 28 | 29 | public: 30 | 31 | // You cannot default construct Functions--you must use `parse`. 32 | Function() = delete; 33 | 34 | // Executes this function, returning the result of the execution. 35 | Value run(); 36 | 37 | // Returns debugging information about this type. 38 | std::ostream& dump(std::ostream& out) const; 39 | 40 | // Attempts to parse a `Function` instance from the `string_view`. 41 | // 42 | // If the first character of `view` isn't a known `Function` name, `nullptr` is returned. 43 | static std::optional parse(std::string_view& view); 44 | 45 | // Registers a new funciton with the given name, arity, and function pointer. 46 | // 47 | // Any previous function associated with `name` will be silently discarded. 48 | static void register_function(char name, size_t arity, funcptr_t func); 49 | 50 | // Registers all builtin functions. 51 | static void initialize(); 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /javascript/src/literal.js: -------------------------------------------------------------------------------- 1 | import { Value } from './value.js'; 2 | 3 | /** 4 | * An abstract class that's used to indicate a type is a wrapper around a 5 | * JavaScript built-in. 6 | * 7 | * @template {T} 8 | * @abstract 9 | * @see Value - For more information on why we don't simply use built-ins. 10 | */ 11 | export class Literal extends Value { 12 | /** 13 | * The data associated with this Literal. 14 | * 15 | * @protected 16 | * @type {T} 17 | */ 18 | _data; 19 | 20 | /** 21 | * Creates a new `Literal` with the given data. 22 | * 23 | * @param {T} data - The data to associate with this `Literal`. 24 | */ 25 | constructor(data) { 26 | super(); 27 | 28 | this._data = data; 29 | } 30 | 31 | /** 32 | * Running a literal simply returns itself. 33 | * 34 | * @return {Literal} - `this`. 35 | */ 36 | run() { 37 | return this; 38 | } 39 | 40 | /** 41 | * Converts this class's `_data` to a JavaScript `string`. 42 | * 43 | * @return {string} 44 | */ 45 | toString() { 46 | return String(this._data); 47 | } 48 | 49 | /** 50 | * Converts this class's `_data` to a JavaScript `number`. 51 | * 52 | * @return {number} 53 | */ 54 | toNumber() { 55 | return Number(this._data); 56 | } 57 | 58 | /** 59 | * Converts this class's `_data` to a JavaScript `boolean`. 60 | * 61 | * @return {boolean} 62 | */ 63 | toBoolean() { 64 | return Boolean(this._data); 65 | } 66 | 67 | /** 68 | * A `Literal` is only equal to another instance of the same type_, which 69 | * also has the same `_data`. 70 | * 71 | * @param {Value} rhs - The value to compare against `this`. 72 | * @return {boolean} - Whether `rhs` is a `this.constructor`, and its `_data` 73 | * is equivalent to `this`'s. 74 | */ 75 | eql(rhs) { 76 | return rhs instanceof this.constructor && this._data === rhs._data; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/tests/shared.rb: -------------------------------------------------------------------------------- 1 | module Kn 2 | module Test 3 | Variable = Struct.new :ident 4 | Function = Struct.new :ast 5 | end 6 | end 7 | 8 | 9 | module Kn::Test::Shared 10 | class InvalidExpression < Exception 11 | attr_reader :expr 12 | 13 | def initialize(expr) 14 | @expr = expr 15 | super "invalid expression: #{expr.inspect}" 16 | end 17 | end 18 | 19 | def parse(expr) 20 | case expr 21 | when /\ANull\(\)\Z/ then :null 22 | when /\AString\((.*?)\)\Z/m then $1 23 | when /\ABoolean\((true|false)\)\Z/ then $1 == 'true' 24 | when /\ANumber\(((?:-(?!0\)))?\d+)\)\Z/ then $1.to_i # `-0` is invalid. 25 | when /\AFunction\((.*?)\)\Z/m then Kn::Test::Function.new $1 26 | when /\AIdentifier\(([_a-z][_a-z0-9]*)\)\Z/ then Kn::Test::Variable.new $1 27 | else fail "bad expression: #{expr.inspect}" 28 | end 29 | end 30 | 31 | def execute(expr, raise_on_failure: true) 32 | IO.pipe do |r, w| 33 | unless system(*Array($executable_to_test), '-e', expr, out: w, err: :close) 34 | raise InvalidExpression, expr if raise_on_failure 35 | end 36 | 37 | w.close 38 | r.read 39 | end 40 | end 41 | 42 | def assert_fails 43 | assert_raises(InvalidExpression) { yield } 44 | end 45 | 46 | def assert_runs 47 | yield 48 | end 49 | 50 | def dump(expr) 51 | execute("D #{expr}") 52 | end 53 | 54 | def check_ub? 55 | $check_ub 56 | end 57 | 58 | def eval(expr) 59 | parse dump expr 60 | end 61 | 62 | def to_string(expr) 63 | val = eval "+ '' #{expr}" 64 | raise "not a string: #{val.inspect}" unless val.is_a? String 65 | val 66 | end 67 | 68 | def to_number(expr) 69 | val = eval "+ 0 #{expr}" 70 | raise "not a number: #{val.inspect}" unless val.is_a? Integer 71 | val 72 | end 73 | 74 | def to_boolean(expr) 75 | val = eval "! ! #{expr}" 76 | raise "not a boolean: #{val.inspect}" unless val == true || val == false 77 | val 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /test/tests/null.rb: -------------------------------------------------------------------------------- 1 | require 'minitest' 2 | require 'minitest/spec' 3 | require_relative 'shared' 4 | 5 | describe '2.4.1 Null' do 6 | include Kn::Test::Shared 7 | 8 | describe '2.4.1 Contexts' do 9 | it '(boolean) is falsey' do 10 | assert_equal false, to_boolean('NULL') 11 | end 12 | 13 | it '(numeric) is zero' do 14 | assert_equal 0, to_number('NULL') 15 | end 16 | 17 | it '(string) is "null"' do 18 | assert_equal 'null', to_string('NULL') 19 | end 20 | end 21 | 22 | describe 'Parsing' do 23 | it 'parses a simple `N` properly' do 24 | assert_equal :null, eval('N') 25 | end 26 | 27 | it 'does not parse `null`' do 28 | # when its not in scope, it fails. 29 | assert_fails { eval('null') } 30 | 31 | # when it is in scope, its evaluates to what its assigned. 32 | assert_equal 12, eval('; = null 12 : null') 33 | end 34 | 35 | it 'strips trailing keywords properly' do 36 | assert_equal 12, eval(';N12') 37 | assert_equal 12, eval(';NU12') 38 | assert_equal 12, eval(';NUL12') 39 | assert_equal 12, eval(';NULL12') 40 | end 41 | end 42 | 43 | describe 'operators' do 44 | describe '4.3.9 ?' do 45 | it 'equals itself' do 46 | assert_equal true, eval('? NULL NULL') 47 | end 48 | 49 | it 'is not equal to other values' do 50 | assert_equal false, eval('? NULL FALSE') 51 | assert_equal false, eval('? NULL TRUE') 52 | assert_equal false, eval('? NULL 0') 53 | assert_equal false, eval('? NULL ""') 54 | assert_equal false, eval('? NULL "0"') 55 | assert_equal false, eval('? NULL "NULL"') 56 | assert_equal false, eval('? NULL "null"') 57 | end 58 | end 59 | 60 | # describe '<' do 61 | # it 'cannot be compared' do 62 | # assert_fails { eval('< NULL 1') } 63 | # end 64 | # end 65 | 66 | # describe '>' do 67 | # it 'cannot be compared' do 68 | # assert_fails { eval('> NULL 1') } 69 | # end 70 | # end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /javascript/src/bool.js: -------------------------------------------------------------------------------- 1 | import { TYPES } from './value.js'; 2 | import { Literal } from './literal.js'; 3 | 4 | /** 5 | * @typedef {import('./stream.js').Stream} Stream 6 | * @typedef {import('./value.js').Value} Value 7 | */ 8 | 9 | /** 10 | * The boolean type within Knight, used to represent truthfulness. 11 | * 12 | * @see Value - For more information on why we don't simply use `true`/`false`. 13 | * @extends {Literal} 14 | */ 15 | export class Bool extends Literal { 16 | /** 17 | * Attempts to parse a `Bool` from `stream`. 18 | * 19 | * @param {Stream} stream - The stream from which to parse. 20 | * @return {Bool|null} - The parsed `Bool`, or `null` if the stream did not 21 | * start with `T` or `F`. 22 | */ 23 | static parse(stream) { 24 | const match = stream.match(/^([TF])[A-Z]*/, 1); 25 | 26 | return match && new Bool(match === 'T'); 27 | } 28 | 29 | /** 30 | * Provides a debugging representation of this class. 31 | * 32 | * @return {string} 33 | */ 34 | dump() { 35 | return `Boolean(${this})`; 36 | } 37 | 38 | /** 39 | * Checks to see if `this` is less than `rhs`. 40 | * 41 | * This will only return true if `this._data` is false and `rhs.toBoolean()` 42 | * is true. 43 | * 44 | * @param {Value} rhs - The value against which to compare. 45 | * @return {boolean} - Whether or not `this` is less than `rhs`. 46 | */ 47 | lth(rhs) { 48 | return !this._data && rhs.toBoolean(); 49 | } 50 | 51 | /** 52 | * Checks to see if `this` is greater than `rhs`. 53 | * 54 | * This will only return true if `this._data` is true and `rhs.toBoolean()` 55 | * is false. 56 | * 57 | * @param {Value} rhs - The value against which to compare. 58 | * @return {boolean} - Whether or not `this` is greater than `rhs`. 59 | */ 60 | gth(rhs) { 61 | return this._data && !rhs.toBoolean(); 62 | } 63 | } 64 | 65 | // Add the `Bool` class to the list of known types, so it can be parsed. 66 | TYPES.push(Bool); 67 | -------------------------------------------------------------------------------- /ruby/golf.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | class Object;alias call itself;class<< self;alias d define_method end;d(:B){self 3 | };d(:coerce){to_i.coerce _1};d(:to_s){call.to_s};d(:to_sym){to_s.to_sym};d(:to_i 4 | ){to_s.to_i};d(:to_int){to_i};d(:to_str){to_s};d(:P){|*|gets};d(:E){s(+to_s).()} 5 | d(:R){|*|rand 0..0xffff_ffff};d(:!){!call};d(:Q){exit to_i};d(:`){Kernel.`(to_s) 6 | };d(:C){call.()};d(:L){to_s.size};d(:D){call.tap{$><< _1.x}};d(:^){(call**_1.()) 7 | .to_i};d(:+){call+_1.()};d(:-){call-_1.()};d(:O){$><<(s=call.to_s)[..-2];s[-1]!= 8 | ?\\&&puts(s[-1])};d(:*){call*_1.()};d(:/){call/_1.()};d(:%){call%_1.()};d(:|){!( 9 | x=call)?_1.():x};d(:<){call<_1.()};d(:G){to_s[_1.to_i,_2.to_i]};d(:>){call>_1.() 10 | };d(??){(s=call).is_a?((r=_1.()).class)&&s==r};d(:&){!(x=call)?x:_1.()};d(:W){_1 11 | .()until !self;nil};d(?=){$E[self]=_1.()};d(?;){call;_1.()};d(:I){(!self ?_2: _1 12 | ).()};d(:S){t=+to_s;t[_1.to_i,_2.to_i]=_3.to_s;t}end;class String;d(:!){empty?}; 13 | d(:x){"String(#{to_s})"};alias m *;d(:*){m _1.to_i}end;class<< false;d(:>){|_|!1} 14 | d(:!){!0};d(:x){'Boolean(false)'};d(:<){!!_1}end;class<< true;d(:<){|_|!1};d(:>){ 15 | !_1};d(:x){'Boolean(true)'};d(:to_i){1};$E={};d(:!){false}end;class<< nil;d(:!){! 16 | 0};d(:x){'Null()'};d(:to_s){'null'}end;class Symbol;d(:x){"Identifier(#{to_s})"} 17 | d(:call){$E.fetch(self){abort}};d(:to_s){call.to_s}end;class Integer;d(:/){(to_f 18 | ./_1.to_i).truncate};d(:x){"Number(#{to_s})"};d(:!){zero?};end;def s i;i.slice!\ 19 | /\A(?:[\s(){}\[\]:]|#.*?\n)*/;i.slice!(/\A\d+/)?$&.to_i: i.slice!(/\A([TF])[A-Z_ 20 | ]*/)?$1==?T:i.slice!(/\A[a-z_0-9]+/)?$&.to_sym: i.slice!(/\AN[A-Z_]*/)?nil: # / 21 | i.slice!(/\A'([^']*)'|\A"([^"]*)"/)?$+:(a=((i.slice! /\A([A-Z][A-Z_]*|.)/;m=Object 22 | .instance_method$&[0]).arity).succ.times.map{s i};a=[0]if[]==a;proc{m.bind_call *a 23 | })end;%w[-e -f].include?(f=$*.shift)&&(v=$*.shift)&&$*==[]or abort\ 24 | "usage: #$0 (-e 'program' | -f file)";s(f=='-f'?open(v,&:read):+v).() 25 | __END__ 26 | 27 | 1080 wide 28 | 1200 tall 29 | -------------------------------------------------------------------------------- /c/ast-ext/ext/helloworld.c: -------------------------------------------------------------------------------- 1 | #include "../src/custom.h" 2 | #include "../src/function.h" 3 | 4 | struct hello_world { 5 | char *name, *greeting; 6 | unsigned rc; 7 | }; 8 | #define HW(custom) ((struct hello_world *) (custom)->data) 9 | 10 | struct kn_custom *hw_clone(struct kn_custom *custom) { 11 | ++((struct hello_world *) (custom)->data)->rc; 12 | 13 | return custom; 14 | } 15 | 16 | void hw_free(struct kn_custom *custom) { 17 | if (--HW(custom)->rc) return; 18 | free(HW(custom)->name); 19 | free(HW(custom)->greeting); 20 | free(HW(custom)); 21 | free(custom); 22 | } 23 | 24 | void hw_dump(struct kn_custom *custom) { 25 | printf("HelloWorld(\"%s\", \"%s\")", HW(custom)->greeting, HW(custom)->name); 26 | } 27 | 28 | struct kn_string *hw_string(struct kn_custom *custom) { 29 | struct kn_string *string = kn_string_alloc( 30 | strlen(HW(custom)->name) + strlen(HW(custom)->greeting) + 3 31 | ); 32 | 33 | strcpy(kn_string_deref(string), HW(custom)->greeting); 34 | strcat(kn_string_deref(string), ", "); 35 | strcat(kn_string_deref(string), HW(custom)->name); 36 | strcat(kn_string_deref(string), "!"); 37 | 38 | return string; 39 | } 40 | 41 | kn_value_t hw_run(struct kn_custom *custom) { 42 | return kn_value_new_string(hw_string(custom)); 43 | } 44 | 45 | static struct kn_custom_vtable_t hw_vtable = { 46 | .clone = hw_clone, 47 | .free = hw_free, 48 | .dump = hw_dump, 49 | .run = hw_run, 50 | .to_number = NULL, 51 | .to_boolean = NULL, 52 | .to_string = hw_string 53 | }; 54 | 55 | KN_FUNCTION_DECLARE(extension, 1, 'X') { 56 | struct kn_custom *custom = xmalloc(sizeof(struct kn_custom)); 57 | struct hello_world *data = xmalloc(sizeof(struct hello_world)); 58 | struct kn_string *name = kn_value_to_string(args[0]); 59 | 60 | data->name = strdup(kn_string_deref(name)); 61 | kn_string_free(name); 62 | data->greeting = strdup("Hello"); 63 | data->rc = 1; 64 | 65 | custom->data = data; 66 | custom->vtable = &hw_vtable; 67 | 68 | return kn_value_new_custom(custom); 69 | } 70 | -------------------------------------------------------------------------------- /raku/lib/Knight/Number.rakumod: -------------------------------------------------------------------------------- 1 | use Knight::TypedValue; 2 | use Knight::Value; 3 | 4 | #| The number type in Knight. 5 | #| 6 | #| As per the Knights specifications, numbers are integral numbers only. 7 | #| 8 | #| Because implementations are allowed to define the maximum and minimum values, I've chosen to use Raku's 9 | #| `Int` type, as it allows for arbitrary-precision arithmetic. 10 | unit class Knight::Number does Knight::TypedValue[Int, * <=> *, * == *]; 11 | 12 | #| Gets an internal representation of the class; used in debugging. 13 | method gist(--> Str) { 14 | "Number($.Str)"; 15 | } 16 | 17 | #| Converts `$rhs` to an `Int`, then adds it to `self`, returning a new Number. 18 | method add(Knight::Value $rhs, --> ::?CLASS) { 19 | ::?CLASS.new: $!value + $rhs.Int 20 | } 21 | 22 | #| Converts `$rhs` to an `Int`, then subtracts it from `self`, returning a new Number. 23 | method sub(Knight::Value $rhs, --> ::?CLASS) { 24 | ::?CLASS.new: $!value - $rhs.Int 25 | } 26 | 27 | #| Converts `$rhs` to an `Int`, then multiplies `self` by it, returning a new Number. 28 | method mul(Knight::Value $rhs, --> ::?CLASS) { 29 | ::?CLASS.new: $!value * $rhs.Int 30 | } 31 | 32 | #| Converts `$rhs` to an `Int`, then divides `self` by it (rounding down), returning a new Number. 33 | #| 34 | #| `$rhs` may not be zero when converted to an int, or the program will `die`. 35 | method div(Knight::Value $rhs, --> ::?CLASS) { 36 | # We have to use `(x / y).Int`, as `div` rounds incorrectly with negative numbers. 37 | ::?CLASS.new: ($!value / ($rhs.Int or die 'Cannot divide by zero!')).Int 38 | } 39 | 40 | #| Converts `$rhs` to an `Int`, then modulos `self` by it, returning a new Number. 41 | #| 42 | #| `$rhs` may not be zero when converted to an int, or the program will `die`. 43 | method mod(Knight::Value $rhs, --> ::?CLASS) { 44 | ::?CLASS.new: $!value mod ($rhs.Int or die 'Cannot modulo by zero!') 45 | } 46 | 47 | #| Converts `$rhs` to an `Int`, then raises `self` to its power. 48 | method pow(Knight::Value $rhs, --> ::?CLASS) { 49 | ::?CLASS.new: ($!value ** $rhs.Int).Int 50 | } 51 | -------------------------------------------------------------------------------- /c/ast-ext/src/main.c: -------------------------------------------------------------------------------- 1 | #include "knight.h" /* kn_startup, kn_run, kn_value_free, kn_shutdown */ 2 | #include "shared.h" /* die, xmalloc, xrealloc */ 3 | 4 | #include /* strlen */ 5 | #include /* free */ 6 | #include /* FILE, fopen, sterror, feof, fread, fclose */ 7 | #include /* errno */ 8 | 9 | static char *read_file(const char *filename) { 10 | FILE *file = fopen(filename, "r"); 11 | 12 | #ifndef KN_RECKLESS 13 | if (file == NULL) 14 | die("unable to read file '%s': %s", filename, strerror(errno)); 15 | #endif /* !KN_RECKLESS */ 16 | 17 | size_t length = 0; 18 | size_t capacity = 2048; 19 | char *contents = xmalloc(capacity); 20 | 21 | while (!feof(file)) { 22 | size_t amntread = fread(&contents[length], 1, capacity - length, file); 23 | 24 | if (amntread == 0) { 25 | 26 | #ifndef KN_RECKLESS 27 | if (!feof(stdin)) 28 | die("unable to line in file '%s': %s'", filename, strerror(errno)); 29 | #endif /* !KN_RECKLESS */ 30 | 31 | break; 32 | } 33 | 34 | length += amntread; 35 | 36 | if (length == capacity) { 37 | capacity *= 2; 38 | contents = xrealloc(contents, capacity); 39 | } 40 | } 41 | 42 | if (fclose(file) == EOF) { 43 | 44 | #ifndef KN_RECKLESS 45 | perror("couldn't close input file"); 46 | #endif /* !KN_RECKLESS */ 47 | 48 | } 49 | 50 | return xrealloc(contents, length); 51 | } 52 | 53 | static void usage(char *program) { 54 | die("usage: %s (-e 'expr' | -f file)", program); 55 | } 56 | 57 | int main(int argc, char **argv) { 58 | char *str; 59 | 60 | if (argc != 3 || strlen(argv[1]) != 2 || argv[1][0] != '-') 61 | usage(argv[0]); 62 | 63 | switch (argv[1][1]) { 64 | case 'e': 65 | str = argv[2]; 66 | break; 67 | case 'f': 68 | str = read_file(argv[2]); 69 | break; 70 | default: 71 | usage(argv[0]); 72 | } 73 | 74 | kn_startup(); 75 | 76 | #ifdef KN_RECKLESS 77 | kn_run(str); 78 | #else 79 | kn_value_free(kn_run(str)); 80 | kn_shutdown(); 81 | 82 | if (argv[1][1] == 'f') 83 | free(str); 84 | #endif /* KN_RECKLESS */ 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /c/ast/src/env.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_ENV_H 2 | #define KN_ENV_H 3 | 4 | #include "value.h" /* kn_value */ 5 | #include /* bool */ 6 | 7 | /* 8 | * A variable within Knight. 9 | * 10 | * This struct is only returned via `kn_env_fetch`, and lives for the remainder 11 | * of the program's lifetime. (Or, at least until `kn_env_free` is called.) As 12 | * such, there is no need to free it. 13 | */ 14 | struct kn_variable { 15 | /* 16 | * The value associated with this variable. 17 | * 18 | * When a variable is first fetched, this is set to `KN_UNDEFINED`, and 19 | * should be overwritten before being used. 20 | */ 21 | kn_value value; 22 | 23 | /* 24 | * The name of this variable. 25 | */ 26 | const char *name; 27 | }; 28 | 29 | /* 30 | * Initializes the global Knight environment. 31 | * 32 | * This _must_ be called before `kn_env_fetch` is called. 33 | */ 34 | void kn_env_startup(void); 35 | 36 | /* 37 | * Frees all resources associated with the global Knight environment. 38 | * 39 | * This will invalidate all `kn_variable` pointers, and `kn_env_startup` must 40 | * be called again before `kn_env_fetch` can be used. 41 | */ 42 | void kn_env_shutdown(void); 43 | 44 | /* 45 | * Fetches the variable associated with the given identifier. 46 | * 47 | * If ownership of `identifier` is being transferred to this function, `owned` 48 | * should be set to `true`; if simply a reference is being given, it should be 49 | * `false`. 50 | * 51 | * This will always return a `kn_variable`, which may have been newly created. 52 | */ 53 | struct kn_variable *kn_env_fetch(const char *identifier, bool owned); 54 | 55 | /* 56 | * Assigns a value to this variable, overwriting whatever was there previously. 57 | */ 58 | void kn_variable_assign(struct kn_variable *variable, kn_value); 59 | 60 | /* 61 | * Runs the given variable, returning the value associated with it. 62 | * 63 | * If the variable has not been assigned to yet, this will abort the program. 64 | */ 65 | kn_value kn_variable_run(struct kn_variable *variable); 66 | 67 | #endif /* !KN_ENV_H */ 68 | -------------------------------------------------------------------------------- /c/ast/src/main.c: -------------------------------------------------------------------------------- 1 | #include "knight.h" /* kn_startup, kn_run, kn_value_free, kn_shutdown */ 2 | #include "shared.h" /* die, xmalloc, xrealloc */ 3 | 4 | #include /* free, NULL, size_t */ 5 | #include /* FILE, fopen, strerror, feof, fread, fclose, perror, stdin 6 | EOF, */ 7 | #include /* errno */ 8 | 9 | static char *read_file(const char *filename) { 10 | FILE *file = fopen(filename, "r"); 11 | 12 | #ifndef KN_RECKLESS 13 | if (file == NULL) 14 | die("unable to read file '%s': %s", filename, strerror(errno)); 15 | #endif /* !KN_RECKLESS */ 16 | 17 | size_t length = 0; 18 | size_t capacity = 2048; 19 | char *contents = xmalloc(capacity); 20 | 21 | while (!feof(file)) { 22 | size_t amntread = fread(&contents[length], 1, capacity - length, file); 23 | 24 | if (amntread == 0) { 25 | 26 | #ifndef KN_RECKLESS 27 | if (!feof(stdin)) 28 | die("unable to line in file '%s': %s'", filename, strerror(errno)); 29 | #endif /* !KN_RECKLESS */ 30 | 31 | break; 32 | } 33 | 34 | length += amntread; 35 | 36 | if (length == capacity) { 37 | capacity *= 2; 38 | contents = xrealloc(contents, capacity); 39 | } 40 | } 41 | 42 | if (fclose(file) == EOF) { 43 | 44 | #ifndef KN_RECKLESS 45 | perror("couldn't close input file"); 46 | #endif /* !KN_RECKLESS */ 47 | 48 | } 49 | 50 | return xrealloc(contents, length); 51 | } 52 | 53 | static void usage(char *program) { 54 | die("usage: %s (-e 'expr' | -f file)", program); 55 | } 56 | 57 | int main(int argc, char **argv) { 58 | char *str; 59 | 60 | if (argc != 3 || argv[1][0] != '-' || argv[1][1] != 2 || argv[1][0] != '-') 61 | usage(argv[0]); 62 | 63 | switch (argv[1][1]) { 64 | case 'e': 65 | str = argv[2]; 66 | break; 67 | case 'f': 68 | str = read_file(argv[2]); 69 | break; 70 | default: 71 | usage(argv[0]); 72 | } 73 | 74 | kn_startup(); 75 | 76 | #ifdef KN_RECKLESS 77 | kn_run(str); 78 | #else 79 | kn_value_free(kn_run(str)); 80 | kn_shutdown(); 81 | 82 | if (argv[1][1] == 'f') 83 | free(str); 84 | #endif /* KN_RECKLESS */ 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /c/ast/README.md: -------------------------------------------------------------------------------- 1 | # Knight in C 2 | This is the AST-walker implementation of Knight in C. It's (as of April 2021), the most performant version of Knight I've developed. 3 | 4 | # Macros 5 | ## Micro-optimizations 6 | - `KN_ENV_NBUCKETS`: Used to adjust how many buckets the environment uses when hashing. 7 | - `KN_ENV_CAPACITY`: Used to adjust the amount of variables that can exist in each bucket. 8 | - `KN_STRING_PADDING_LENGTH`: Used to adjust the amount of extra padding given to embedded strings. This is generally chosen to be a number that rounds off the string's length to a multiple of two. 9 | 10 | ## Macro-optimizations 11 | - `NDEBUG`: Disables all _internal_ debugging code. This should only be undefined when debugging. 12 | - `KN_RECKLESS`: Assumes that absolutely no problems will occur during the execution of the program. _All_ checks for undefined behaviour are completely removed (including things like "did the `` ` `` function open properly?"). 13 | - `KN_COMPUTED_GOTOS`: Enables the use of computed gotos, which can significantly increase the speed of the parsing functions. However, since this uses nonstandard features, it's not enabled by default. 14 | 15 | ## Experiments 16 | These are experiments I tried out out see if I could make this implementation faster. They may improve performance depending on what you're using knight for, but they don't improve the `timeit` times. 17 | - `KN_STRING_CACHE`: Enable this to cache larger strings, so that their structs don't have to be allocated. Since I started using embedded strings, this has become less relevant. (The `KN_STRING_CACHE_MAXLEN` macro can be used to control the maximum length string that will be cached, and `KN_STRING_CACHE_LINESIZE` is the amount of strings per cache.) 18 | 19 | ## Extensions 20 | - `KN_EXT_VALUE`: Enables the use of the `VALUE` function, which looks up a variable indirectly based on its argument. 21 | - `KN_EXT_NEGATE`: Enables the use of the `~` function, which simply converts its argument to a number and negates it. 22 | - `KN_EXT_EQL_INTERPOLATE`: Allows the use of non-identifiers on the LHS of `=`, which will be coerced to an identifier. 23 | - `KN_EXT_CUSTOM_TYPES` 24 | - `KN_EXT_FUNCTION` -------------------------------------------------------------------------------- /haskell/src/Parser.hs: -------------------------------------------------------------------------------- 1 | {- The `Parser` type and its associated functions. -} 2 | module Parser where 3 | 4 | import Data.Char (isDigit, isSpace, isLower) 5 | import Control.Applicative 6 | import Data.Functor 7 | 8 | -- | A Parser that's used within Knight. 9 | -- 10 | -- There are standard library things that can probably do this better, but I 11 | -- wrote it myself to learn how Haskell works. 12 | newtype Parser a = Parser { parse :: String -> (String, Maybe a) } 13 | 14 | {- Implementation for `Parser`. -} 15 | instance Functor Parser where 16 | fmap f (Parser p) = Parser $ \s -> 17 | let (s', mf) = p s 18 | in (s', f <$> mf) 19 | 20 | instance Applicative Parser where 21 | pure x = Parser $ \s -> (s, Just x) 22 | Parser pf <*> Parser px = Parser $ \s0 -> 23 | let (s1, mf) = pf s0 24 | (s2, mx) = px s1 25 | in (s2, mf <*> mx) 26 | 27 | instance Alternative Parser where 28 | empty = Parser $ \s -> (s, Nothing) 29 | Parser p <|> Parser q = Parser $ \s -> 30 | case (p s, q s) of 31 | (lhs@(_, Just _), _) -> lhs 32 | (_, rhs) -> rhs 33 | 34 | instance Monad Parser where 35 | Parser p >>= f = Parser $ \s -> 36 | let (s', thing) = p s 37 | in case thing of 38 | Just x -> let Parser pf = f x in pf s' 39 | Nothing -> (s', Nothing) 40 | 41 | -- | The basic parsing function. 42 | -- 43 | -- This parses a single character if the function returns True. 44 | satisfy :: (Char -> Bool) -> Parser Char 45 | satisfy f = Parser p 46 | where 47 | p (x:xs) | f x = (xs, Just x) 48 | p text = (text, Nothing) 49 | 50 | -- | Parses the given character. 51 | char :: Char -> Parser Char 52 | char c = satisfy (== c) 53 | 54 | -- | Parses a single digit. 55 | digit :: Parser Char 56 | digit = satisfy isDigit 57 | 58 | -- | Parses a single whitespace character. 59 | space :: Parser Char 60 | space = satisfy isSpace 61 | 62 | -- | Parses a lower case letter. 63 | lower :: Parser Char 64 | lower = satisfy isLower 65 | 66 | -- | Parses end of line (`\n`). 67 | eol :: Parser () 68 | eol = void $ char '\n' 69 | 70 | -- | Parses the given string. 71 | string :: String -> Parser String 72 | string = traverse char 73 | -------------------------------------------------------------------------------- /javascript/src/null.js: -------------------------------------------------------------------------------- 1 | import { TYPES } from './value.js'; 2 | import { RuntimeError } from './error.js'; 3 | import { Literal } from './literal.js'; 4 | 5 | /** 6 | * @typedef {import('./stream.js').Stream} Stream 7 | * @typedef {import('./value.js').Value} Value 8 | */ 9 | 10 | /** 11 | * The null type within Knight, used to represent the lack of a value. 12 | * 13 | * @see Value - For more information on why we don't simply use `null`. 14 | * @extends {Literal} 15 | */ 16 | export class Null extends Literal { 17 | /** 18 | * Attempts to parse a `Null` from the `stream`. 19 | * 20 | * @param {Stream} stream - The stream from which to parse. 21 | * @return {Null|null} - The parsed `Null`, or `null` if the stream did not 22 | * start with a `Null`. 23 | */ 24 | static parse(stream) { 25 | return stream.match(/^N[A-Z]*/) && new Null(); 26 | } 27 | 28 | /** Creates a new `Null`. */ 29 | constructor(){ 30 | super(null); 31 | } 32 | 33 | /** 34 | * Provides a debugging representation of this class. 35 | * 36 | * @return {string} 37 | */ 38 | dump() { 39 | return 'Null()'; 40 | } 41 | 42 | /** 43 | * Returns whether or not `this` is equal to `rhs`. 44 | * 45 | * This is simply a more efficient overload of the `eql` function, as we 46 | * only need to check to see if `rhs` is `Null`. 47 | * 48 | * @override 49 | * @param {Value} The value against which to compare. 50 | * @return {boolean} Whether `rhs` is `Null`. 51 | */ 52 | eql(rhs) { 53 | return rhs instanceof Null; 54 | } 55 | 56 | /** 57 | * Comparisons with `Null` are invalid, and this always fails. 58 | * 59 | * @param {Value} _rhs - Ignored. 60 | * @throws {RuntimeError} This is always thrown. 61 | */ 62 | lth(_rhs) { 63 | throw new RuntimeError("Cannot compare Null."); 64 | } 65 | 66 | /** 67 | * Comparisons with `Null` are invalid, and this always fails. 68 | * 69 | * @param {Value} _rhs - Ignored. 70 | * @throws {RuntimeError} This is always thrown. 71 | */ 72 | gth(_rhs) { 73 | throw new RuntimeError("Cannot compare Null."); 74 | } 75 | } 76 | 77 | // Add the `Null` class to the list of known types, so it can be parsed. 78 | TYPES.push(Null); 79 | -------------------------------------------------------------------------------- /ruby/function.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Kn 4 | class Function 5 | @funcs = {} 6 | 7 | def self.register(name, &block) 8 | raise ArgumentError, 'name must be exactly one character' unless name.length == 1 9 | @funcs[name] = block 10 | end 11 | 12 | def self.[](name) = @funcs[name] 13 | 14 | def initialize(func, name, args) 15 | @func = func 16 | @name = name 17 | @args = args 18 | end 19 | 20 | def inspect = "Function(#@name#{@args.map { ", #{_1.inspect}" }.join})" 21 | 22 | def run = @func.call(*@args) 23 | 24 | undef == 25 | undef to_s 26 | 27 | def respond_to_missing?(...) = run.respond_to_missing?(...) 28 | def method_missing(...) = run.public_send(...) 29 | 30 | register 'P' do String.new gets.chomp end 31 | register 'R' do Number.new rand 0..0xffff_ffff end 32 | 33 | register 'E' do Kn.run _1 end 34 | register 'B' do _1 end 35 | register 'C' do _1.run.run end 36 | register '`' do String.new `#{p(_1.to_s)}` end 37 | register 'Q' do exit _1.to_i end 38 | register '!' do Boolean.new !_1.truthy? end 39 | register 'L' do Number.new _1.to_s.length end 40 | register 'D' do print _1.run.inspect end 41 | register 'O' do 42 | str = _1.to_s 43 | 44 | if str[-1] == '\\' 45 | print str[0..-2] 46 | else 47 | puts str 48 | end 49 | 50 | Null.new 51 | end 52 | 53 | register '+' do _1 + _2 end 54 | register '-' do _1 - _2 end 55 | register '*' do _1 * _2 end 56 | register '/' do _1 / _2 end 57 | register '%' do _1 % _2 end 58 | register '^' do _1 ** _2 end 59 | register '?' do Boolean.new _1.run == _2.run end 60 | register '<' do Boolean.new _1 < _2 end 61 | register '>' do Boolean.new _1 > _2 end 62 | register '&' do (l = _1.run).truthy? ? _2.run : l end 63 | register '|' do (l = _1.run).truthy? ? l : _2.run end 64 | register ';' do _1.run; _2.run end 65 | register '=' do _1.value = _2.run end 66 | register 'W' do _2.run while _1.truthy? ; Null.new end 67 | 68 | register 'I' do _1.truthy? ? _2.run : _3.run end 69 | register 'G' do String.new _1.to_s[_2.to_i, _3.to_i] end 70 | 71 | register 'S' do 72 | str = _1.to_s.dup 73 | str[_2.to_i, _3.to_i] = _4.to_s 74 | String.new str 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /c/c-old/src/string.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_STRING_H 2 | #define KN_STRING_H 3 | 4 | /* 5 | * A struct that's used to manage strings within Knight. 6 | * 7 | * Because all strings are immutable in Knight, we're able to use a simple 8 | * reference counting scheme to determine when a string needs to be freed. 9 | * 10 | * Strings that shouldn't be reference counted (such as string literals) should 11 | * be passed to the `kn_string_intern` function. 12 | */ 13 | struct kn_string_t { 14 | const char *str; 15 | unsigned *rc; 16 | }; 17 | 18 | /* 19 | * Creates an interned string. 20 | * 21 | * An interned string is one that's used internally by quest for identifiers 22 | * and other things that last the lifetime of the program. Any strings passed to 23 | * it will never be freed. 24 | * 25 | * This string should not be used directly with after passing it to this 26 | * function. Instead, interact with it through the struct field. 27 | * 28 | * When done using the returned value, it should be passed to `kn_string_free` 29 | * to prevent memory leaks. 30 | */ 31 | struct kn_string_t kn_string_intern(const char *str); 32 | 33 | /* 34 | * Creates a new string. 35 | * 36 | * This should only be called with strings that are able to be `free`d. For 37 | * static strings, look at `kn_string_intern`. 38 | * 39 | * This string should not be used directly with after passing it to this 40 | * function. Instead, interact with it through the struct field. 41 | * 42 | * When done using the returned value, it should be passed to `kn_string_free` 43 | * to prevent memory leaks. 44 | */ 45 | struct kn_string_t kn_string_new(const char *str); 46 | 47 | /* 48 | * Clones a string, returning a new copy of it. 49 | * 50 | * When done using the returned value, it should be passed to `kn_string_free` 51 | * to prevent memory leaks. 52 | */ 53 | struct kn_string_t kn_string_clone(const struct kn_string_t *string); 54 | 55 | /* 56 | * Frees the memory associated with a string. 57 | * 58 | * This function should be called whenever a string's done being used to prevent 59 | * memory leaks. 60 | * 61 | * This does nothing if the string was created via `kn_string_intern`. 62 | */ 63 | void kn_string_free(struct kn_string_t *string); 64 | 65 | #endif /* KN_STRING_H */ 66 | -------------------------------------------------------------------------------- /javascript/src/stream.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The source code of a Knight program. 3 | * 4 | * When parsing Knight source code, there needs to be a way to communicate what 5 | * part of the source code was parsed. However, because JavaScript does not have 6 | * out parameters, nor does it allow for the modification of strings, we must 7 | * use a class to maintain the remaining source code to be parsed. Thus, the 8 | * `Stream` class. 9 | * 10 | * @see Value.parse 11 | */ 12 | export class Stream { 13 | /** @type {string} */ 14 | #source; 15 | 16 | /** 17 | * Creates a new stream with the given source. 18 | * @param {string} source - The source of the stream. 19 | */ 20 | constructor(source) { 21 | this.#source = source; 22 | } 23 | 24 | /** 25 | * Strips the leading whitespace and comments from `this`. 26 | */ 27 | stripWhitespace() { 28 | // simply ignore the return value--it can fail for all we care. 29 | this.match(/^([\]\[\s(){}:]+|#[^\n]*(\n|$))*/); 30 | } 31 | 32 | /** 33 | * Peeks at the first character of the stream, without consuming it 34 | * 35 | * @return {string|null} - The first character of `this`, or `null` empty. 36 | */ 37 | peek() { 38 | return this.#source[0] || null; 39 | } 40 | 41 | /** 42 | * Attempts to match the given `regex` at the start of the stream, returning 43 | * the `group`th group if successful. 44 | * 45 | * @param {RegExp} regex - The regular expression to match, which should have 46 | * an `^` (so as to only match the stream start). 47 | * @param {number} [group] - The group number to return; the default (0) 48 | * returns the entire match. 49 | * @return {string|null} - Returns the matched group, or `null` if no match. 50 | */ 51 | match(regex, group=0) { 52 | const match = regex.exec(this.#source); 53 | 54 | if (match === null) { 55 | return null; 56 | } 57 | 58 | // remove the match from the source code. 59 | this.#source = this.#source.substr(match[0].length); 60 | 61 | return match[group]; 62 | } 63 | 64 | /** 65 | * Returns the remainder of the stream to be parsed. 66 | * 67 | * @return {string} - The remainder of the stream. 68 | */ 69 | toString() { 70 | return this.#source; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /c/ast-ext/src/env.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_ENV_H 2 | #define KN_ENV_H 3 | 4 | #include "value.h" /* kn_value */ 5 | #include /* size_t */ 6 | #include /* bool */ 7 | 8 | /* 9 | * A variable within Knight. 10 | * 11 | * This struct is only returned via `kn_env_fetch`, and lives for the remainder 12 | * of the program's lifetime. (Or, at least until `kn_env_free` is called.) As 13 | * such, there is no need to free it. 14 | */ 15 | struct kn_variable { 16 | /* 17 | * The value associated with this variable. 18 | * 19 | * When a variable is first fetched, this is set to `KN_UNDEFINED`, and 20 | * should be overwritten before being used. 21 | */ 22 | kn_value value; 23 | 24 | /* 25 | * The name of this variable. 26 | */ 27 | const char *name; 28 | }; 29 | 30 | /* 31 | * Initializes the global Knight environment with the given starting capacity. 32 | * 33 | * This _must_ be called before `kn_env_fetch` is called. 34 | */ 35 | void kn_env_startup(void); 36 | 37 | /* 38 | * Frees all resources associated with the global Knight environment. 39 | * 40 | * This will invalidate all `kn_variable` pointers, and `kn_env_startup` must 41 | * be called again before `kn_env_fetch` can be used. 42 | */ 43 | void kn_env_shutdown(void); 44 | 45 | /* 46 | * Fetches the variable associated with the given identifier. 47 | * 48 | * If ownership of `identifier` is being transferred to this function, `owned` 49 | * should be set to `true`; if simply a reference is being given, it should be 50 | * `false`. 51 | * 52 | * This will always return a `kn_variable`, which may have been newly created. 53 | */ 54 | struct kn_variable *kn_env_fetch(const char *identifier, bool owned); 55 | 56 | /* 57 | * Assigns a value to this variable, overwriting whatever was there previously. 58 | */ 59 | void kn_variable_assign(struct kn_variable *variable, kn_value); 60 | 61 | /* 62 | * Runs the given variable, returning the value associated with it. 63 | * 64 | * If the variable has not been assigned to yet, this will abort the program. 65 | */ 66 | kn_value kn_variable_run(struct kn_variable *variable); 67 | 68 | /* 69 | * Fetches the name of the variable. 70 | */ 71 | const char *kn_variable_name(const struct kn_variable *variable); 72 | 73 | #endif /* !KN_ENV_H */ 74 | -------------------------------------------------------------------------------- /python/knight/string.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from knight import Value, Stream, Literal, ParseError 3 | from typing import Union 4 | import re 5 | 6 | class String(Literal[bool]): 7 | """ 8 | The number class in Knight. 9 | 10 | As per the Knight specs, the only number type within Knight is 11 | integral numbers. As such, we use Python's builtin `int` class. 12 | """ 13 | 14 | BEGIN_REGEX: re.Pattern = re.compile(r'[\'\"]') 15 | SINGLE_REGEX: re.Pattern = re.compile(r"([^']*)'") 16 | DOUBLE_REGEX: re.Pattern = re.compile(r'([^"]*)"') 17 | INT_REGEX: re.Pattern = re.compile(r'^\s*[-+]?\d+') 18 | 19 | @classmethod 20 | def parse(cls, stream: Stream) -> Union[None, String]: 21 | """ 22 | Parses a `String` from the `stream`, returning `None` if the 23 | nothing can be parsed. 24 | 25 | If a starting quote is matched and no ending quote is, then a 26 | `ParseError` will be raised. 27 | """ 28 | quote = stream.matches(String.BEGIN_REGEX) 29 | 30 | if not quote: 31 | return None 32 | 33 | regex = String.SINGLE_REGEX if quote == "'" else String.DOUBLE_REGEX 34 | body = stream.matches(regex, 1) 35 | 36 | if body is None: 37 | raise ParseError(f'unterminated string encountered: {stream}') 38 | else: 39 | return String(body) 40 | 41 | def __int__(self) -> int: 42 | """ 43 | Converts `self` to an integer, as per the Knight specs. 44 | 45 | Note that this is different from Python's conversions, as invalid 46 | numbers do not cause exceptions to be thrown, but rather handles 47 | them in a specific fashion. See the Knight specs for details. 48 | """ 49 | match = String.INT_REGEX.match(self.data) 50 | 51 | return int(match[0]) if match else 0 52 | 53 | def __add__(self, rhs: Value) -> String: 54 | """ Concatenates `self` and `rhs` """ 55 | return String(f'{self}{rhs}') 56 | 57 | def __mul__(self, rhs: Value) -> int: 58 | """ Repeats `self` for `rhs` times """ 59 | return String(str(self) * int(rhs)) 60 | 61 | def __lt__(self, rhs: Value) -> bool: 62 | """ 63 | Checks to see if `self` is lexicographically less than `rhs`. 64 | """ 65 | return self.data < str(rhs) 66 | 67 | def __gt__(self, rhs: Value) -> bool: 68 | """ 69 | Checks to see if `self` is lexicographically less than `rhs`. 70 | """ 71 | return self.data > str(rhs) 72 | -------------------------------------------------------------------------------- /c/ast-ext/src/custom.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_CUSTOM_H 2 | #define KN_CUSTOM_H 3 | #ifdef KN_EXT_CUSTOM_TYPES 4 | 5 | #include /* NULL, free */ 6 | #include "value.h" /* kn_value, kn_number, kn_boolean, kn_string */ 7 | 8 | /* 9 | * The virtual table for custom types. 10 | * 11 | * There should generally only be one of these per type. Functions may be 12 | * omitted (by assigning them to `NULL`); if they are, their default 13 | * implementations will be used. 14 | * 15 | * Note that if `run` is omitted and not all of `to_number`, `to_boolean`, and 16 | * `to_string` are defined, an infinite loop will occur. 17 | */ 18 | struct kn_custom_vtable { 19 | /* 20 | * Duplicates `data`, returning a new instance of it. 21 | * 22 | * The return will be passed to `free` in addition to `data`. This function 23 | * is required and does not have a default value. 24 | */ 25 | void *(*clone)(void *data); 26 | 27 | /* 28 | * Releases the resources associated with `data`. 29 | * 30 | * The default implementation simply calls the stdlib's `free`. 31 | */ 32 | void (*free)(void *data); 33 | 34 | /* 35 | * Dumps debugging info for `data`. 36 | * 37 | * The default implementation writes `Custom(, )` to 38 | * stdout. 39 | */ 40 | void (*dump)(void *data); 41 | 42 | /* 43 | * Executes the given `data`, returning the value associated with it. 44 | * 45 | * The default implementation simply calls `clone` and duplicates it. 46 | */ 47 | kn_value (*run)(void *data); 48 | 49 | /* 50 | * Converts the `data` to a number. 51 | * 52 | * The default implementation will call `run`, and then convert the result 53 | * to a number. 54 | */ 55 | kn_number (*to_number)(void *data); 56 | 57 | /* 58 | * Converts the `data` to a boolean. 59 | * 60 | * The default implementation will call `run`, and then convert the result 61 | * to a boolean. 62 | */ 63 | kn_boolean (*to_boolean)(void *data); 64 | 65 | /* 66 | * Converts the `data` to a string. 67 | * 68 | * The default implementation will call `run`, and then convert the result 69 | * to a string. 70 | */ 71 | struct kn_string *(*to_string)(void *data); 72 | }; 73 | 74 | struct kn_custom { 75 | void *data; 76 | const struct kn_custom_vtable *vtable; 77 | }; 78 | 79 | #endif /* KN_EXT_CUSTOM_TYPES */ 80 | #endif /* !KN_CUSTOM_H */ 81 | -------------------------------------------------------------------------------- /javascript/src/ident.js: -------------------------------------------------------------------------------- 1 | import { Value, TYPES } from './value.js'; 2 | import { RuntimeError } from './error.js'; 3 | 4 | /** 5 | * @typedef {import('./stream.js').Stream} Stream 6 | */ 7 | 8 | /** 9 | * The list of all known identifiers. 10 | * 11 | * @private 12 | * @type {Object.} 13 | */ 14 | const ENVIRONMENT = {}; 15 | 16 | /** 17 | * The identifier class within Knight. 18 | * 19 | * As per the specs, all identifiers are global. As such, there is no scoping 20 | * whatsoever: A single `ENVIRONMENT` is used. 21 | */ 22 | export class Ident extends Value { 23 | /** @type {string} */ 24 | #ident; 25 | 26 | /** 27 | * Attempts to parse an `Ident` from the `stream`. 28 | * 29 | * @param {Stream} stream - The stream with which to parse. 30 | * @return {Ident|null} - The parsed `Ident`, or `null` if the stream did not 31 | * start with an `Ident`. 32 | */ 33 | static parse(stream) { 34 | const match = stream.match(/^[a-z_][a-z0-9_]*/); 35 | 36 | return match && new Ident(match); 37 | } 38 | 39 | /** 40 | * Creates a new `Ident` with the given name. 41 | * 42 | * @param {string} ident - The name of this identifier. 43 | */ 44 | constructor(ident) { 45 | super(); 46 | 47 | this.#ident = ident; 48 | } 49 | 50 | /** 51 | * Provides a debugging representation of this class. 52 | * 53 | * @return {string} 54 | */ 55 | dump() { 56 | return `Identifier(${this.#ident})`; 57 | } 58 | 59 | /** 60 | * Associates `value` with this class. 61 | * 62 | * Any previously associated `Value` is simply discarded. 63 | * 64 | * @param {Value} value - The value to associate with this identifier's name. 65 | */ 66 | assign(value) { 67 | ENVIRONMENT[this.#ident] = value; 68 | } 69 | 70 | /** 71 | * Executes this identifier, returning its last assigned value. 72 | * 73 | * @return {Value} - The last value associated with this identifier's name. 74 | * @throws {RuntimeError} - Thrown if this identifier was never assigned to. 75 | */ 76 | run() { 77 | const value = ENVIRONMENT[this.#ident]; 78 | 79 | if (value === undefined) { 80 | throw new RuntimeError(`Unknown identifier '${this.#ident}'`); 81 | } else { 82 | return value; 83 | } 84 | } 85 | } 86 | 87 | // Add the `Ident` class to the list of known types, so it can be parsed. 88 | TYPES.push(Ident); 89 | -------------------------------------------------------------------------------- /haskell/stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # 15 | # The location of a snapshot can be provided as a file or url. Stack assumes 16 | # a snapshot provided as a file might change, whereas a url resource does not. 17 | # 18 | # resolver: ./custom-snapshot.yaml 19 | # resolver: https://example.com/snapshots/2018-01-01.yaml 20 | resolver: lts-16.17 21 | 22 | # User packages to be built. 23 | # Various formats can be used as shown in the example below. 24 | # 25 | # packages: 26 | # - some-directory 27 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 28 | # subdirs: 29 | # - auto-update 30 | # - wai 31 | packages: 32 | - . 33 | # Dependency packages to be pulled from upstream that are not in the resolver. 34 | # These entries can reference officially published versions as well as 35 | # forks / in-progress versions pinned to a git hash. For example: 36 | # 37 | # extra-deps: 38 | # - acme-missiles-0.3 39 | # - git: https://github.com/commercialhaskell/stack.git 40 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 41 | # 42 | # extra-deps: [] 43 | 44 | # Override default flag values for local packages and extra-deps 45 | # flags: {} 46 | 47 | # Extra package databases containing global packages 48 | # extra-package-dbs: [] 49 | 50 | # Control whether we use the GHC we find on the path 51 | # system-ghc: true 52 | # 53 | # Require a specific version of stack, using version ranges 54 | # require-stack-version: -any # Default 55 | # require-stack-version: ">=2.3" 56 | # 57 | # Override the architecture used by stack, especially useful on Windows 58 | # arch: i386 59 | # arch: x86_64 60 | # 61 | # Extra directories used by stack for building 62 | # extra-include-dirs: [/path/to/dir] 63 | # extra-lib-dirs: [/path/to/dir] 64 | # 65 | # Allow a newer minor version of GHC than the snapshot specifies 66 | # compiler-check: newer-minor 67 | -------------------------------------------------------------------------------- /raku/lib/Knight/Value.rakumod: -------------------------------------------------------------------------------- 1 | #| All types in Knight are `Value`s. 2 | #| 3 | #| Unlike Raku, Knight does not have separate operators for integral and string-like types: There's one "add" operator, 4 | #| one "multiplication" operator, one "equality" operator, etc. As such, overloading operators with Knight types would 5 | #| not be very idiomatic: One would expect `Knight::String.new("12") + Knight::String.new("34")` to return a 6 | #| `Knight::Integer.new(46)`, _not_ `Knight::String.new("1234")`. Thus, I've opted to use methods, such as `add`, `mul`, 7 | #| `eql`, etc. 8 | unit role Knight::Value; 9 | 10 | #| Assigns a value to `self`. 11 | #| 12 | #| All values (asides from `Identifier`s) are first converted to `Str`s, after which an `Identifier` is created and 13 | #| assigned the given `$value`. 14 | method assign(Knight::Value $value, --> Knight::Value) { 15 | require Knight::Identifier; 16 | ::('Knight::Identifier').new($.Str).assign: $value 17 | } 18 | 19 | #| Checks to see if `self` is less than `$rhs`. 20 | #| 21 | #| This method simply checks to see if `cmp` is `Less`. 22 | method lth(Knight::Value $rhs, --> Bool) { 23 | $.cmp($rhs) === Less 24 | } 25 | 26 | #| Checks to see if `self` is greater than `$rhs`. 27 | #| 28 | #| This method simply checks to see if `cmp` is `More`. 29 | method gth(Knight::Value $rhs, --> Bool) { 30 | $.cmp($rhs) === More 31 | } 32 | 33 | #| Executes `self` and returns its result. 34 | #| 35 | #| For literals, such as `Number`s and `String`s, this simply returns `self`. For more complex types, such as 36 | #| `Identifier` and `Function`, this should actually execute the value and return its result. 37 | method run(--> Knight::Value) { … } 38 | 39 | #| Compares `self` to `$rhs`, returning an `Order` determining if it's less than, greater, or equal to the other. 40 | #| 41 | #| This method is only used for the `gth` and `lth` functions; notably not `eql`. As such, it should coerce `$rhs` to 42 | #| the proper type. 43 | method cmp(Knight::Value $rhs, --> Order) { … } 44 | 45 | #| Checks to see if `self` is equal to `$rhs`. 46 | #| 47 | #| As per the Knight specs, `eql` is the only function that does not automatically coerce the `$rhs`. As such, `$rhs` 48 | #| must be the same type as `self`. Implementors should also use `multi` 49 | multi method eql(Knight::Value $rhs, --> False) is pure { } 50 | 51 | #| All `Value`s must be convertible to an `Int`. 52 | method Int(--> Int) { … } 53 | 54 | #| All `Value`s must be convertible to a `Str`. 55 | method Str(--> Str) { … } 56 | 57 | #| All `Value`s must be convertible to a `Bool`. 58 | method Bool(--> Bool) { … } 59 | -------------------------------------------------------------------------------- /python/knight/number.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | from knight import Value, Stream, Literal, RunError 3 | from typing import Union 4 | import re 5 | import math 6 | 7 | class Number(Literal[int]): 8 | """ 9 | The number class in Knight. 10 | 11 | As per the Knight specs, the only number type within Knight is 12 | integral numbers. As such, we use Python's builtin `int` class. 13 | """ 14 | REGEX: re.Pattern = re.compile(r'^\d+') 15 | 16 | @classmethod 17 | def parse(cls, stream: Stream) -> Union[None, Number]: 18 | """ 19 | Parses a Number out from the stream. 20 | 21 | This returns `None` if the stream doesn't start with a digit. 22 | """ 23 | if match := stream.matches(Number.REGEX): 24 | return cls(int(match)) 25 | else: 26 | return None 27 | 28 | def __add__(self, rhs: Value) -> Number: 29 | """ Converts `rhs` to an `int` and adds it to `self.` """ 30 | return Number(int(self) + int(rhs)) 31 | 32 | def __sub__(self, rhs: Value) -> Number: 33 | """ Converts `rhs` to an `int` and subtracts it from `self.` """ 34 | return Number(int(self) - int(rhs)) 35 | 36 | def __mul__(self, rhs: Value) -> Number: 37 | """ Converts `rhs` to an `int` and multiples it by it `self.` """ 38 | return Number(int(self) * int(rhs)) 39 | 40 | def __floordiv__(self, rhs: Value) -> Number: 41 | """ 42 | Converts `rhs` to an `int` and divides `self` by it, with the 43 | division operation conforming to the Knight specs. 44 | 45 | This will raise a `RunError` if `rhs` is zero. 46 | """ 47 | if rhs := int(rhs): 48 | return Number(math.trunc(int(self) / rhs)) 49 | else: 50 | raise RunError('Cannot divide by zero!') 51 | 52 | def __mod__(self, rhs: Value) -> Number: 53 | """ 54 | Converts `rhs` to an `int` and modulos `self` by it, with the 55 | modulo operation conforming to the Knight specs. 56 | 57 | This will raise a `RunError` if `rhs` is zero. 58 | """ 59 | if rhs := int(rhs): 60 | return Number(int(self) % rhs) 61 | else: 62 | raise RunError('Cannot divide by zero!') 63 | 64 | def __pow__(self, rhs: Value) -> Number: 65 | """ 66 | Converts `rhs` to an `int` and exponentiates `self` by it, with 67 | the power ofoperation conforming to the Knight specs. 68 | """ 69 | return Number(math.trunc(int(self) ** int(rhs))) 70 | 71 | def __lt__(self, rhs: Value) -> bool: 72 | """ Checks to see if `self` is numerically less than `rhs`. """ 73 | return int(self) < int(rhs) 74 | 75 | def __gt__(self, rhs: Value) -> bool: 76 | """ Checks to see if `self` is numerically greater than `rhs`. """ 77 | return int(self) > int(rhs) 78 | -------------------------------------------------------------------------------- /c/c-old/src/ast.h: -------------------------------------------------------------------------------- 1 | #ifndef KN_AST_H 2 | #define KN_AST_H 3 | 4 | #include "value.h" /* kn_value_t */ 5 | #include "function.h" /* kn_function_t */ 6 | 7 | /* 8 | * The kinds of tokens that exist within Knight. 9 | * 10 | * Technically, there's only three "real" token types: values, identifiers, and 11 | * functions. However, for ease of use, each function is defined to be its own 12 | * token as well. 13 | * 14 | * The `value` and `identifier` fields are exclusively used by the `KN_TT_VALUE` 15 | * and `KN_TT_IDENTIFIER` fields, respectively. The `arguments` field is used by 16 | * the function tokens. Arguments start from `arguments[0]` and count upwards to 17 | * one less than the arity of the function; arguments beyond that are undefined. 18 | * 19 | * With the sole exception of `KN_TT_EQL`, every function will automatically 20 | * convert types to the types they expect. 21 | */ 22 | enum kn_token_kind { 23 | /* 24 | * A literal value---ie a `kn_value_t`. 25 | */ 26 | KN_TT_VALUE, 27 | 28 | /* 29 | * An identifier within Knight. 30 | */ 31 | KN_TT_IDENTIFIER, 32 | 33 | /* 34 | * A function within knight, ie a `kn_function_t` and its arguments. 35 | */ 36 | KN_TT_FUNCTION 37 | }; 38 | 39 | /* 40 | * The type that represents executable code within Knight. 41 | * 42 | * This type is created via `kn_ast_parse` and should be disposed of via 43 | * `kn_ast_free`. 44 | */ 45 | struct kn_ast_t { 46 | enum kn_token_kind kind; 47 | 48 | union { 49 | struct kn_value_t value; 50 | struct { 51 | size_t *refcount; 52 | union { 53 | const char *identifier; 54 | struct { 55 | const struct kn_function_t *function; 56 | struct kn_ast_t *arguments; 57 | }; 58 | }; 59 | 60 | }; 61 | }; 62 | }; 63 | 64 | /* 65 | * Parse a `kn_ast_t` from an input stream. 66 | * 67 | * Aborts if the stream's empty, or no valid tokens can be parsed. 68 | */ 69 | struct kn_ast_t kn_ast_parse(const char **stream); 70 | 71 | /* 72 | * Runs an `kn_ast_t`, returning the value associated with it. 73 | * 74 | * If any errors occur whilst running the tree, the function will abort the 75 | * program with a message indicating the error. 76 | */ 77 | struct kn_value_t kn_ast_run(const struct kn_ast_t *ast); 78 | 79 | /* 80 | * Dumps debugging information about the AST to stdout. 81 | */ 82 | void kn_ast_dump(const struct kn_ast_t *ast); 83 | 84 | /* 85 | * Clones an `kn_ast_t`. 86 | */ 87 | struct kn_ast_t kn_ast_clone(const struct kn_ast_t *ast); 88 | 89 | /* 90 | * Releases all the resources the `kn_ast_t` has associated with it. 91 | */ 92 | void kn_ast_free(struct kn_ast_t *ast); 93 | 94 | #endif /* KN_AST_H */ 95 | -------------------------------------------------------------------------------- /asm/string.s: -------------------------------------------------------------------------------- 1 | /* Create a new string */ 2 | .globl kn_string_new 3 | kn_string_new: 4 | push %rbx 5 | mov %rdi, %rbx // preserve the string passed to us. 6 | 7 | mov $12, %edi // 8 bytes for string ptr, 4 for refcount 8 | call xmalloc // create a new string struct 9 | mov %rbx, (%rax) // move string ptr over 10 | movl $1, 8(%rax) // set refc to 1. 11 | 12 | pop %rbx 13 | ret 14 | 15 | // free the memory associated with a string. 16 | .globl kn_string_free 17 | kn_string_free: 18 | cmpl $0, 8(%rdi) 19 | jz 0f // if it is an interned string, dont free it. 20 | decl 8(%rdi) // otherwise, subtract one from the rc 21 | jz 1f // and if we are at zero, free the string 22 | 0: 23 | ret 24 | 1: 25 | push %rbx 26 | mov %rdi, %rbx 27 | mov (%rdi), %rdi 28 | call _free // first, free the associated string. 29 | mov %rbx, %rdi 30 | pop %rbx 31 | jmp _free // now free the entire string struct. 32 | 33 | 34 | // .globl kn_string_free 35 | // kn_string_free: 36 | // movl 8(%rdi), %ecx 37 | // jecxz 0f // if it is an interned string, dont free it. 38 | // retn 39 | // dec %ecx // otherwise, subtract one from the rc 40 | // jecxz 1f // and if we are at zero, free the string 41 | // mov %ecx, 8(%rdi) // otherwise, put the updated rc back and return. 42 | // 0: 43 | // ret 44 | // 1: 45 | // push %rbx 46 | // mov %rdi, %rbx 47 | // mov (%rdi), %rdi 48 | // call _free // first, free the associated string. 49 | // mov %rbx, %rdi 50 | // pop %rbx 51 | // jmp _free // now free the entire string struct. 52 | 53 | // Duplicate the string 54 | .globl kn_string_clone 55 | kn_string_clone: 56 | mov %rdi, %rax 57 | cmpl $0, 8(%rdi) // check to see if we are interned 58 | jne 0f 59 | incq 8(%rdi) // if we are not, add one to the refcount. 60 | 0: 61 | ret 62 | 63 | .data 64 | 65 | .balign 16 66 | .globl kn_string_empty 67 | kn_string_empty: 68 | .quad string_intern_empty 69 | .quad 0 70 | 71 | .balign 16 72 | .globl kn_string_true 73 | kn_string_true: 74 | .quad string_intern_true 75 | .quad 0 76 | 77 | .balign 16 78 | .globl kn_string_false 79 | kn_string_false: 80 | .quad string_intern_false 81 | .quad 0 82 | 83 | .balign 16 84 | .globl kn_string_null 85 | kn_string_null: 86 | .quad string_intern_null 87 | .quad 0 88 | 89 | .balign 16 90 | .globl kn_string_zero 91 | kn_string_zero: 92 | .quad string_intern_zero 93 | .quad 0 94 | 95 | string_intern_empty: .asciz "" 96 | string_intern_true: .asciz "true" 97 | string_intern_false: .asciz "false" 98 | string_intern_null: .asciz "null" 99 | string_intern_zero: .asciz "0" 100 | -------------------------------------------------------------------------------- /asm/old/string.s: -------------------------------------------------------------------------------- 1 | /* Create a new string */ 2 | .globl kn_string_new 3 | kn_string_new: 4 | push %rbx 5 | mov %rdi, %rbx // preserve the string passed to us. 6 | 7 | mov $12, %edi // 8 bytes for string ptr, 4 for refcount 8 | call xmalloc // create a new string struct 9 | mov %rbx, (%rax) // move string ptr over 10 | movl $1, 8(%rax) // set refc to 1. 11 | 12 | pop %rbx 13 | ret 14 | 15 | // free the memory associated with a string. 16 | .globl kn_string_free 17 | kn_string_free: 18 | cmpl $0, 8(%rdi) 19 | jz 0f // if it is an interned string, dont free it. 20 | decl 8(%rdi) // otherwise, subtract one from the rc 21 | jz 1f // and if we are at zero, free the string 22 | 0: 23 | ret 24 | 1: 25 | push %rbx 26 | mov %rdi, %rbx 27 | mov (%rdi), %rdi 28 | call _free // first, free the associated string. 29 | mov %rbx, %rdi 30 | pop %rbx 31 | jmp _free // now free the entire string struct. 32 | 33 | 34 | // .globl kn_string_free 35 | // kn_string_free: 36 | // movl 8(%rdi), %ecx 37 | // jecxz 0f // if it is an interned string, dont free it. 38 | // retn 39 | // dec %ecx // otherwise, subtract one from the rc 40 | // jecxz 1f // and if we are at zero, free the string 41 | // mov %ecx, 8(%rdi) // otherwise, put the updated rc back and return. 42 | // 0: 43 | // ret 44 | // 1: 45 | // push %rbx 46 | // mov %rdi, %rbx 47 | // mov (%rdi), %rdi 48 | // call _free // first, free the associated string. 49 | // mov %rbx, %rdi 50 | // pop %rbx 51 | // jmp _free // now free the entire string struct. 52 | 53 | // Duplicate the string 54 | .globl kn_string_clone 55 | kn_string_clone: 56 | mov %rdi, %rax 57 | cmpl $0, 8(%rdi) // check to see if we are interned 58 | jne 0f 59 | incq 8(%rdi) // if we are not, add one to the refcount. 60 | 0: 61 | ret 62 | 63 | .data 64 | 65 | .balign 16 66 | .globl kn_string_empty 67 | kn_string_empty: 68 | .quad string_intern_empty 69 | .quad 0 70 | 71 | .balign 16 72 | .globl kn_string_true 73 | kn_string_true: 74 | .quad string_intern_true 75 | .quad 0 76 | 77 | .balign 16 78 | .globl kn_string_false 79 | kn_string_false: 80 | .quad string_intern_false 81 | .quad 0 82 | 83 | .balign 16 84 | .globl kn_string_null 85 | kn_string_null: 86 | .quad string_intern_null 87 | .quad 0 88 | 89 | .balign 16 90 | .globl kn_string_zero 91 | kn_string_zero: 92 | .quad string_intern_zero 93 | .quad 0 94 | 95 | string_intern_empty: .asciz "" 96 | string_intern_true: .asciz "true" 97 | string_intern_false: .asciz "false" 98 | string_intern_null: .asciz "null" 99 | string_intern_zero: .asciz "0" 100 | -------------------------------------------------------------------------------- /test/tests/variable.rb: -------------------------------------------------------------------------------- 1 | require 'minitest' 2 | require 'minitest/spec' 3 | require_relative 'shared' 4 | 5 | describe 'Variable' do 6 | include Kn::Test::Shared 7 | 8 | def ident(x) Kn::Test::Variable.new(x) end 9 | 10 | describe 'conversions' do 11 | # Identifiers don't define conversions of their own. 12 | # Rather, they're ran by other code, which converts them. 13 | end 14 | 15 | # Note that identifiers 16 | describe 'parsing' do 17 | =begin 18 | it 'must start with an lower-case letter or an underscore' do 19 | assert_equal ident('_'), eval('BLOCK _') 20 | assert_equal ident('a'), eval('BLOCK a') 21 | assert_equal ident('b'), eval('BLOCK b') 22 | assert_equal ident('z'), eval('BLOCK z') 23 | refute_kind_of Kn::Test::Variable, eval('BLOCK 0') # digits are not identifiers 24 | refute_kind_of Kn::Test::Variable, eval('BLOCK R') # upper case letters are not identifiers. 25 | end 26 | 27 | it 'can have numbers afterwards' do 28 | assert_equal ident('_ab01_2'), eval('BLOCK _ab01_2') 29 | assert_equal ident('foobar_12_baz'), eval('BLOCK foobar_12_baz') 30 | assert_equal ident('__array_123'), eval('BLOCK __array_123') 31 | end 32 | 33 | it 'ignores trailing capital letters' do 34 | assert_equal ident('fizz'), eval('BLOCK fizzBuzz') 35 | assert_equal ident('f'), eval('BLOCK fOO') 36 | assert_equal ident('_'), eval('BLOCK _XYZ') 37 | assert_equal ident('ae1'), eval('BLOCK ae1NOPE') 38 | end 39 | =end 40 | end 41 | 42 | describe 'assignment and retrieval' do 43 | it 'must can be assigned to anything' do 44 | assert_equal :null, eval('= foo NULL') 45 | assert_equal true, eval('= foo TRUE') 46 | assert_equal false, eval('= foo FALSE') 47 | assert_equal 'hi', eval('= foo "hi"') 48 | assert_equal 123, eval('= foo 123') 49 | # assert_kind_of Kn::Test::Function, eval('= foo BLOCK + 1 2') 50 | end 51 | 52 | it 'must return the rhs, but evaluated.' do 53 | assert_equal 123, eval('= foo (+ 120 3)') 54 | assert_equal 15, eval('; = x 3 : = foo * x 5') 55 | assert_equal 15, eval('; = x 15 : = foo x') 56 | end 57 | 58 | =begin 59 | it 'wont evaluate its value when it is executed' do 60 | assert_kind_of Kn::Test::Function, eval('; = foo BLOCK + 1 2 : foo') 61 | assert_equal ident('bar'), eval('; = foo BLOCK bar : foo') 62 | end 63 | =end 64 | 65 | it 'will fail on an unknown Variable' do 66 | assert_fails { eval('unknown') } 67 | end 68 | end 69 | 70 | describe 'operators' do 71 | describe 'CALL' do 72 | =begin 73 | it 'must return its value when called, but not execute it.' do 74 | assert_equal ident('baz'), eval('; = foo BLOCK baz ; = bar BLOCK foo : CALL bar') 75 | end 76 | =end 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /test/tests/block.rb: -------------------------------------------------------------------------------- 1 | require 'minitest' 2 | require 'minitest/spec' 3 | require_relative 'shared' 4 | 5 | describe 'Block' do 6 | include Kn::Test::Shared 7 | 8 | describe 'conversions' do 9 | # Blocks cannot be converted to anything 10 | end 11 | 12 | describe 'parsing' do 13 | it 'takes any argument type' do 14 | assert_runs { execute 'BLOCK 1' } 15 | assert_runs { execute 'BLOCK "a"' } 16 | assert_runs { execute 'BLOCK TRUE' } 17 | assert_runs { execute 'BLOCK FALSE' } 18 | assert_runs { execute 'BLOCK NULL' } 19 | assert_runs { execute 'BLOCK B 3' } 20 | assert_runs { execute 'BLOCK ident' } 21 | end 22 | 23 | it 'requires an argument' do 24 | return pass if $all_ub 25 | assert_fails { execute 'BLOCK' } 26 | assert_fails { execute 'BLOCK BLOCK' } 27 | end 28 | 29 | it 'strips trailing keywords properly' do 30 | assert_runs { execute 'B1' } 31 | assert_runs { execute 'BL!1' } 32 | assert_runs { execute 'BLO!1' } 33 | assert_runs { execute 'BLO RANDOM' } 34 | assert_runs { execute 'BLO RANDOM' } 35 | assert_runs { execute 'BLO RANDOM' } 36 | assert_runs { execute 'BLOa' } 37 | assert_runs { execute 'BLa' } 38 | assert_runs { execute 'Ba' } 39 | end 40 | end 41 | 42 | it 'wont execute its body until called' do 43 | assert_runs { execute 'BLOCK QUIT 1' } 44 | assert_runs { execute 'BLOCK missing' } 45 | assert_runs { execute 'BLOCK EVAL "nope"' } 46 | end 47 | 48 | # note that `BLOCK` simply returns its argument, unevaluated. But in the case of 49 | # literals, this is the same as the literal itself, so we must provide a function istead. 50 | describe 'operators' do 51 | describe 'CALL' do 52 | it 'executes its body' do 53 | assert_equal 12, eval('CALL BLOCK + 5 7') 54 | assert_equal 18, eval('; = foo BLOCK + bar 5 ; = bar 13 : CALL foo') 55 | end 56 | 57 | 58 | it 'can be called with any type' do 59 | # we call these because they may be implemented as a function. 60 | assert_equal 1, eval('CALL BLOCK 1') 61 | assert_equal 'foo', eval('CALL BLOCK "foo"') 62 | assert_equal true, eval('CALL BLOCK TRUE') 63 | assert_equal false, eval('CALL BLOCK FALSE') 64 | assert_equal :null, eval('CALL BLOCK NULL') 65 | assert_equal 1, eval('; = ident 1 : CALL BLOCK ident') 66 | assert_equal 3, eval('CALL BLOCK + 1 2') 67 | end 68 | end 69 | =begin 70 | describe '?' do 71 | it 'is only equivalent to _the exact instance_' do 72 | assert_equal true, eval('; = x B R : ? x x') 73 | end 74 | 75 | it 'is not equal to anything else' do 76 | assert_equal false, eval('? B (! TRUE) B (! TRUE)') 77 | assert_equal false, eval('? B (! TRUE) FALSE') 78 | end 79 | end 80 | =end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /c/ast-ext/README.md: -------------------------------------------------------------------------------- 1 | # Knight in C 2 | This is the AST-walker implementation of Knight in C. It's (as of April 2021), the most performant version of Knight I've developed. 3 | 4 | # Macros 5 | ## Micro-optimizations 6 | - `KN_ENV_NBUCKETS`: Used to adjust how many buckets the environment uses when hashing. 7 | - `KN_ENV_CAPACITY`: Used to adjust the amount of variables that can exist in each bucket. 8 | - `KN_STRING_PADDING_LENGTH`: Used to adjust the amount of extra padding given to embedded strings. This is generally chosen to be a number that rounds off the string's length to a multiple of two. 9 | 10 | ## Macro-optimizations 11 | - `NDEBUG`: Disables all _internal_ debugging code. This should only be undefined when debugging. 12 | - `KN_RECKLESS`: Assumes that absolutely no problems will occur during the execution of the program. _All_ checks for undefined behaviour are completely removed (including things like "did the `` ` `` function open properly?"). 13 | - `KN_COMPUTED_GOTOS`: Enables the use of computed gotos, which can significantly increase the speed of the parsing functions. However, since this uses nonstandard features, it's not enabled by default. 14 | 15 | ## Experiments 16 | These are experiments I tried out out see if I could make this implementation faster. They may improve performance depending on what you're using knight for, but they don't improve the `timeit` times. 17 | - `KN_DYNMAIC_ARGC`: Collapses consecutive functions into the same struct. That is, instead of `; a ; b ; c` being represented as `; (a) (; (b) (; (c)))`, it's represented as `; (a) (b) (c)`. This is only implemented for `;`. 18 | - `KN_ARENA_ALLOCATE`: Allocates string _structs_ in arenas. This is only usable on systems with `mmap`. Since a decent amount of strings are embedded, this can be used to improve performance. However, I didn't see any significant changes, so this was not further explored really. (The `KN_NUM_PAGES` macro can be used to override the amount of pages.) 19 | - `KN_STRING_CACHE`: Enable this to cache larger strings, so that their structs don't have to be allocated. Since I started using embedded strings, this has become less relevant. (The `KN_STRING_CACHE_MAXLEN` macro can be used to control the maximum length string that will be cached, and `KN_STRING_CACHE_LINESIZE` is the amount of strings per cache.) 20 | 21 | ## Extensions 22 | - `KN_EXT_VALUE`: Enables the use of the `VALUE` function, which looks up a variable indirectly based on its argument. 23 | - `KN_EXT_NEGATE`: Enables the use of the `~` function, which simply converts its argument to a number and negates it. 24 | - `KN_EXT_EQL_INTERPOLATE`: Allows the use of non-identifiers on the LHS of `=`, which will be coerced to an identifier. 25 | - `KN_EXT_CUSTOM_TYPES` 26 | - `KN_EXT_FUNCTION` -------------------------------------------------------------------------------- /php/src/Boolean.php: -------------------------------------------------------------------------------- 1 | match('[TF][A-Z]*'); 18 | 19 | return $match ? new self($match[0] === 'T') : null; 20 | } 21 | 22 | /** 23 | * This Boolean's value. 24 | * 25 | * @var bool 26 | **/ 27 | private $data; 28 | 29 | /** 30 | * Create a new Boolean with the given value. 31 | * 32 | * @param bool $val The value of this Boolean. 33 | **/ 34 | public function __construct(bool $val) 35 | { 36 | $this->data = $val; 37 | } 38 | 39 | /** 40 | * Converts this Boolean to a string. 41 | * 42 | * @return string Either "true" or "false", depending on whether this is true or false. 43 | **/ 44 | public function __toString(): string 45 | { 46 | return $this->data ? 'true' : 'false'; 47 | } 48 | 49 | /** 50 | * Converts this Boolean to an int. 51 | * 52 | * @return int Either 0 or 1, depending on whether this is true or false. 53 | **/ 54 | public function toInt(): int 55 | { 56 | return (int) $this->data; 57 | } 58 | 59 | /** 60 | * Converts this Boolean to a bool. 61 | * 62 | * @return bool Simply returns the data associated with this class. 63 | **/ 64 | public function toBool(): bool 65 | { 66 | return $this->data; 67 | } 68 | 69 | /** 70 | * Gets a string representation of this class, for debugging purposes. 71 | * 72 | * @return string 73 | **/ 74 | public function dump(): string 75 | { 76 | return "Boolean($this)"; 77 | } 78 | 79 | /** 80 | * Converts the $rhs to an boolean, then compares $this to it. 81 | * 82 | * This will only return `true` if `$this` is false and `$rhs` is true, or `$this` is true and `$rhs` is false. 83 | * 84 | * @param Value $rhs The boolean to compare to. 85 | * @return int Returns a number less than, equal to, or greater than 0, depending on if `$rhs`, after conversion to 86 | * an int, is less than, equal to, or greater than `$this`. 87 | **/ 88 | protected function cmp(Value $rhs): int 89 | { 90 | return $this->data <=> $rhs->toBool(); 91 | } 92 | 93 | /** 94 | * Checks to see if `$value` is a `Boolean` and equal to `$this`. 95 | * 96 | * @return bool 97 | **/ 98 | public function eql(Value $value): bool 99 | { 100 | return is_a($value, get_class()) && $this->data === $value->data; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /php/src/Value.php: -------------------------------------------------------------------------------- 1 | strip(); 34 | 35 | foreach (Value::TYPES as $class) { 36 | if (!is_null($value = $class::parse($stream))) { 37 | return $value; 38 | } 39 | } 40 | 41 | return null; 42 | } 43 | 44 | /** 45 | * Converts this value to a int. 46 | * 47 | * @return int 48 | **/ 49 | abstract public function toInt(): int; 50 | 51 | /** 52 | * Converts this value to a bool. 53 | * 54 | * @return bool 55 | **/ 56 | abstract public function toBool(): bool; 57 | 58 | /** 59 | * Gets a string representation of this class, for debugging purposes. 60 | * 61 | * @return string 62 | **/ 63 | abstract public function dump(): string; 64 | 65 | /** 66 | * Checks to see if `$this` is equal to `$value`. 67 | * 68 | * @return bool 69 | **/ 70 | abstract public function eql(Value $value): bool; 71 | 72 | /** 73 | * Executes this Value. 74 | * 75 | * By default, the return value is simply `$this`. 76 | * 77 | * @return Value The result of running this value. 78 | **/ 79 | public function run(): Value 80 | { 81 | return $this; 82 | } 83 | 84 | /** 85 | * Checks to see if this value is less than the other. 86 | * 87 | * This calls the `cmp` and then checks to make sure the value is less than zero. 88 | * 89 | * @param Value $rhs The value to test against. 90 | * @return bool Returns `true` if `$this` is less than `$rhs`. 91 | */ 92 | public function lth(Value $rhs): bool 93 | { 94 | return $this->cmp($rhs) < 0; 95 | } 96 | 97 | /** 98 | * Checks to see if this value is greater than the other. 99 | * 100 | * This calls the `cmp` and then checks to make sure the value is greater than zero. 101 | * 102 | * @param Value $rhs The value to test against. 103 | * @return bool Returns `true` if `$this` is greater than `$rhs`. 104 | */ 105 | public function gth(Value $rhs): bool 106 | { 107 | return $this->cmp($rhs) > 0; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /c/vm/src/env.c: -------------------------------------------------------------------------------- 1 | #include /* strcmp, strdup */ 2 | #include /* assert */ 3 | #include 4 | #include "env.h" /* prototypes, value_t, value_free */ 5 | #include "shared.h" /* xmalloc, xrealloc */ 6 | 7 | struct env_bucket_t { 8 | size_t capacity, length; 9 | variable_t *pairs; 10 | }; 11 | 12 | #define NBUCKETS 256 13 | static struct env_bucket_t BUCKETS[NBUCKETS]; 14 | 15 | void env_init(size_t capacity) { 16 | assert(capacity <= SIZE_MAX / sizeof(value_t)); 17 | assert(capacity != 0); 18 | 19 | for (size_t i = 0; i < NBUCKETS; ++i) 20 | BUCKETS[i] = (struct env_bucket_t) { 21 | .capacity = capacity, 22 | .length = 0, 23 | .pairs = xmalloc(sizeof(variable_t [capacity])) 24 | }; 25 | 26 | } 27 | 28 | void env_free() { 29 | struct env_bucket_t *bucket; 30 | 31 | for (size_t i = 0; i < NBUCKETS; ++i) { 32 | bucket = &BUCKETS[i]; 33 | 34 | for (size_t len = 0; len < bucket->length; ++len) { 35 | free((char *) bucket->pairs[len].name); 36 | if (bucket->pairs[len].value != UNDEFINED) 37 | value_free(bucket->pairs[len].value); 38 | } 39 | 40 | free(bucket->pairs); 41 | bucket->length = 0; 42 | bucket->capacity = 0; 43 | } 44 | } 45 | 46 | static struct env_bucket_t *get_bucket(const char *identifier) { 47 | assert(identifier != NULL); 48 | 49 | // This is the MurmurHash. 50 | unsigned long hash = 525201411107845655; 51 | 52 | while (*identifier != '\0') { 53 | hash ^= *identifier++; 54 | hash *= 0x5bd1e9955bd1e995; 55 | hash ^= hash >> 47; 56 | } 57 | 58 | return &BUCKETS[hash & (NBUCKETS - 1)]; 59 | } 60 | 61 | 62 | static variable_t *get_pair( 63 | const struct env_bucket_t *bucket, 64 | const char *identifier 65 | ) { 66 | for (size_t i = 0; i < bucket->length; ++i) { 67 | if (strcmp(bucket->pairs[i].name, identifier) == 0) { 68 | return &bucket->pairs[i]; 69 | } 70 | } 71 | 72 | 73 | return NULL; 74 | } 75 | 76 | 77 | 78 | const char *env_name_for(value_t *value) { 79 | return ((variable_t *) value)->name; 80 | } 81 | 82 | variable_t *env_fetch(const char *identifier, bool owned) { 83 | struct env_bucket_t *bucket = get_bucket(identifier); 84 | variable_t *pair = get_pair(bucket, identifier); 85 | 86 | if (pair != NULL) { 87 | if (owned) 88 | free((char *) identifier); 89 | return pair; 90 | } 91 | 92 | if (bucket->length == bucket->capacity) { 93 | assert(bucket->capacity != 0); 94 | 95 | bucket->capacity *= 2; 96 | bucket->pairs = xrealloc( 97 | bucket->pairs, 98 | sizeof(variable_t [bucket->capacity]) 99 | ); 100 | } 101 | 102 | if (!owned) 103 | identifier = strdup(identifier); 104 | 105 | // note we retain ownership of the ident. 106 | bucket->pairs[bucket->length] = (variable_t) { 107 | .name = identifier, 108 | .value = UNDEFINED 109 | }; 110 | 111 | return &bucket->pairs[bucket->length++]; 112 | } 113 | -------------------------------------------------------------------------------- /perl/lib/Kn/Value.pm: -------------------------------------------------------------------------------- 1 | package Kn::Value; 2 | use strict; 3 | use warnings; 4 | 5 | use overload 6 | '""' => sub { ${shift->run()} }, 7 | 'bool' => sub { ${shift->run()} }, # this is redundant really. 8 | '0+' => sub { ${shift->run()} }; 9 | 10 | # Creates a new `Value` (or whatever subclasses it) by simply getting a 11 | # reference to the second argument. 12 | sub new { 13 | my ($class, $data) = @_; 14 | bless \$data, $class; 15 | } 16 | 17 | # Adds two Values together by converting them both to numbers. 18 | sub add { 19 | Kn::Number->new(int(shift) + int(shift)); 20 | } 21 | 22 | # Subtract two Values by converting them both to numbers. 23 | sub sub { 24 | Kn::Number->new(int(shift) - int(shift)); 25 | } 26 | 27 | # Multiply two Values by converting them both to numbers. 28 | sub mul { 29 | Kn::Number->new(int(shift) * int(shift)); 30 | } 31 | 32 | # Divides the first number by the second, `die`ing if the second's zero. 33 | sub div { 34 | my $lhs = int shift; 35 | my $rhs = int shift; 36 | 37 | die "cant divide by zero" unless $rhs; 38 | 39 | Kn::Number->new(int($lhs / $rhs)); 40 | } 41 | 42 | # Modulo the first number by the second, `die`ing if the second's zero. 43 | sub mod { 44 | my $lhs = int shift; 45 | my $rhs = int shift; 46 | 47 | die "cant modulo by zero" unless $rhs; 48 | 49 | Kn::Number->new($lhs % $rhs); 50 | } 51 | 52 | # Raises the first number to the power of the second. 53 | sub pow { 54 | Kn::Number->new(int(int(shift) ** int(shift))); 55 | } 56 | 57 | # Converts both values to integers and compares them. 58 | sub cmp { 59 | int(shift) <=> int(shift); 60 | } 61 | 62 | # Checks to see if the first argument is less than the second. 63 | sub lth { 64 | shift->cmp(shift) < 0 65 | } 66 | 67 | # Checks to see if the first argument is greater than the second. 68 | sub gth { 69 | shift->cmp(shift) > 0 70 | } 71 | 72 | # Checks to see if the first argument is equal to the second by comparing their 73 | # types and inner data. 74 | sub eql { 75 | my ($lhs, $rhs) = @_; 76 | 77 | ref($lhs) eq ref($rhs) && $$lhs == $$rhs 78 | } 79 | 80 | # Running a normal value simply returns it. 81 | sub run { 82 | shift; 83 | } 84 | 85 | use Kn::Number; 86 | use Kn::Boolean; 87 | use Kn::Identifier; 88 | use Kn::Null; 89 | use Kn::String; 90 | use Kn::Ast; 91 | 92 | # Parses a Value from the stream, stripping leading whitespace and comments. 93 | # If the first character of the stream is invalid, the program `die`s. 94 | sub parse { 95 | my $stream = $_[1]; 96 | my $ret; 97 | 98 | while ($$stream =~ s/\A(?:[\s()\[\]{}:]+|#[^\n]*)//) { 99 | # do nothing, we're stripping the steram. 100 | } 101 | 102 | for (qw(Kn::Number Kn::Identifier Kn::Null Kn::String Kn::Boolean Kn::Ast)){ 103 | $ret = $_->parse($stream); 104 | return $ret if defined $ret; 105 | } 106 | 107 | die "unknown token start '" . substr($$stream, 0, 1) . "'"; 108 | } 109 | 110 | 1; 111 | -------------------------------------------------------------------------------- /javascript/src/value.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('./stream.js').Stream} Stream 3 | */ 4 | 5 | /** 6 | * The list of known types; used when parsing `Value`s. 7 | * 8 | * When parsing, this array will be traversed in order; the first matching 9 | * value will be returned. 10 | * 11 | * @type {Type[]} 12 | * @see Value.parse 13 | */ 14 | export const TYPES = []; 15 | 16 | /** 17 | * The parent class for all entities within Knight source code. 18 | * 19 | * While JavaScript itself has builtin types for booleans, numbers, strings, 20 | * and null, their semantics do not _exactly_ match Knight's: e.g., converting 21 | * `" a"` to a number in Knight should return `0`, where in JavaScript it is 22 | * `NaN`. Another exmaple is how, in Knight, `+ ` should 23 | * convert the `` to a number, whereas in JavaScript it will not if 24 | * `` is a string. 25 | * 26 | * As such, I've opted for have these wrapper classes. An alternative to this 27 | * could have been to have th `Func`tions define the semantics for each type 28 | * through a `switch` statement. However, that doesn't really feel like 29 | * idiomatic JavaScript, so I've opted for this. 30 | * 31 | * @abstract 32 | */ 33 | export class Value { 34 | /** 35 | * Attempts to parse out a new value from the given `stream`. 36 | * 37 | * @param {Stream} stream - The stream to parse. 38 | * @return {Value|null} - Returns the parsed value, or `null` if nothing 39 | * could be parsed. 40 | */ 41 | static parse(stream) { 42 | stream.stripWhitespace(); 43 | 44 | for (var i = 0; i < TYPES.length; i++) { 45 | const match = TYPES[i].parse(stream); 46 | 47 | if (match) { 48 | return match; 49 | } 50 | } 51 | 52 | return null; 53 | } 54 | 55 | /** 56 | * Returns the result of evaluating `this`. 57 | * 58 | * @abstract 59 | */ 60 | run() { 61 | throw new Error; 62 | } 63 | 64 | /** 65 | * Returns debugging information for `this`. 66 | * 67 | * @abstract 68 | * @return {string} 69 | */ 70 | debug() { 71 | throw new Error; 72 | } 73 | 74 | /** 75 | * Converts this Value to a JavaScript string. 76 | * 77 | * The default implementation simply calls `toString()` on `run()`'s result. 78 | * 79 | * @return {string} 80 | */ 81 | toString() { 82 | return this.run().toString(); 83 | } 84 | 85 | /** 86 | * Converts this Value to a JavaScript number. 87 | * 88 | * The default implementation simply calls `toNumber()` on `run()`'s result. 89 | * 90 | * @return {number} 91 | */ 92 | toNumber() { 93 | return this.run().toNumber(); 94 | } 95 | 96 | /** 97 | * Converts this Value to a JavaScript boolean. 98 | * 99 | * The default implementation simply calls `toBoolea()` on `run()`'s result. 100 | * 101 | * @return {boolean} 102 | */ 103 | toBoolean() { 104 | return this.run().toBoolean(); 105 | } 106 | } 107 | --------------------------------------------------------------------------------