├── .gitignore ├── LICENSE ├── README.md ├── examples └── nand-lang-fibs.rkt ├── info.rkt ├── main.rkt ├── nand lang.png ├── nand-parser.rkt ├── nand-runtime.rkt └── nand-transform.rkt /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 rain1 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | # What is this 4 | 5 | This is a quick and rough implementation of Jellonator's [Nandlang](https://github.com/Jellonator/Nandlang) as a racket #lang using the [racket-peg](https://github.com/rain-1/racket-peg) library! 6 | 7 | It's done in 3 parts: 8 | 9 | * The [parser](https://github.com/rain-1/nand-lang/blob/master/nand-parser.rkt) processes the nand language input files, producing a lispy abstract syntax tree. 10 | * The [transformer](https://github.com/rain-1/nand-lang/blob/master/nand-transform.rkt) translates the abstract syntax tree into regular racket code, implementing the semantics of the language by embedding it in another language. 11 | * The [runtime](https://github.com/rain-1/nand-lang/blob/master/nand-runtime.rkt) provides functions for the translated output to make use of during execution. 12 | 13 | The same approach could be used to implement any other language. 14 | 15 | # Installation 16 | 17 | Make sure you have the latest version of racket-peg installed. 18 | 19 | ``` 20 | raco pkg remove nand-lang 21 | raco pkg install -u -t dir nand-lang 22 | ``` 23 | 24 | # Example 25 | 26 | ![alt text](/nand%20lang.png?raw=true) 27 | -------------------------------------------------------------------------------- /examples/nand-lang-fibs.rkt: -------------------------------------------------------------------------------- 1 | #lang nand-lang 2 | 3 | // not logic gate 4 | function not(in : out) { 5 | out = in ! in; 6 | } 7 | 8 | // and logic gate 9 | function and(a, b : out) { 10 | out = not(a ! b); 11 | } 12 | 13 | // or logic gate 14 | function or(a, b : out) { 15 | out = not(a) ! not(b); 16 | } 17 | 18 | // xor logic gate 19 | function xor(a, b : out) { 20 | out = or(and(a, not(b)), and(not(a), b)); 21 | } 22 | 23 | // returns true if a and b are equal 24 | function eq(a, b : out) { 25 | out = not(xor(a, b)); 26 | } 27 | 28 | // full adder 29 | function add(a, b, cin : v, cout) { 30 | v = xor(cin, xor(a, b)); 31 | cout = or(and(a, b), and(xor(a, b), cin)); 32 | } 33 | 34 | // 8 bit adder 35 | function add8( 36 | a1, a2, a3, a4, a5, a6, a7, a8, 37 | b1, b2, b3, b4, b5, b6, b7, b8 38 | : o1, o2, o3, o4, o5, o6, o7, o8) { 39 | var c = 0; 40 | o8, c = add(a8, b8, c); 41 | o7, c = add(a7, b7, c); 42 | o6, c = add(a6, b6, c); 43 | o5, c = add(a5, b5, c); 44 | o4, c = add(a4, b4, c); 45 | o3, c = add(a3, b3, c); 46 | o2, c = add(a2, b2, c); 47 | o1, c = add(a1, b1, c); 48 | } 49 | 50 | // returns the two's complement of the given integer 51 | function complement8( 52 | i1, i2, i3, i4, i5, i6, i7, i8 53 | : o1, o2, o3, o4, o5, o6, o7, o8) { 54 | o1, o2, o3, o4, o5, o6, o7, o8 = add8( 55 | not(i1), not(i2), not(i3), not(i4), not(i5), not(i6), not(i7), not(i8), 56 | 0, 0, 0, 0, 0, 0, 0, 1); 57 | } 58 | 59 | // 8 bit subtraction 60 | function sub8( 61 | a1, a2, a3, a4, a5, a6, a7, a8, 62 | b1, b2, b3, b4, b5, b6, b7, b8 63 | : o1, o2, o3, o4, o5, o6, o7, o8) { 64 | o1, o2, o3, o4, o5, o6, o7, o8 = add8( 65 | a1, a2, a3, a4, a5, a6, a7, a8, 66 | complement8(b1, b2, b3, b4, b5, b6, b7, b8)); 67 | } 68 | 69 | // 8 bit equality 70 | function equal8( 71 | a1, a2, a3, a4, a5, a6, a7, a8, 72 | b1, b2, b3, b4, b5, b6, b7, b8 73 | : out) { 74 | out = and( 75 | and(and(eq(a1, b1), eq(a2, b2)), and(eq(a3, b3), eq(a4, b4))), 76 | and(and(eq(a5, b5), eq(a6, b6)), and(eq(a7, b7), eq(a8, b8)))); 77 | } 78 | 79 | // returns the Fibonacci number for the given input 80 | function fibonacci( 81 | i1, i2, i3, i4, i5, i6, i7, i8 82 | : o1, o2, o3, o4, o5, o6, o7, o8) { 83 | var is_equal = equal8(i1, i2, i3, i4, i5, i6, i7, 0, 0, 0, 0, 0, 0, 0, 0, 0); 84 | if is_equal { 85 | // return input if equal to 1 or 0 86 | o1 = i1; 87 | o2 = i2; 88 | o3 = i3; 89 | o4 = i4; 90 | o5 = i5; 91 | o6 = i6; 92 | o7 = i7; 93 | o8 = i8; 94 | } 95 | if not(is_equal) { 96 | // o = fibonacci(i - 1) + fibonacci(i - 2); 97 | o1, o2, o3, o4, o5, o6, o7, o8 = add8( 98 | fibonacci(sub8(i1, i2, i3, i4, i5, i6, i7, i8, 0, 0, 0, 0, 0, 0, 0, 1)), 99 | fibonacci(sub8(i1, i2, i3, i4, i5, i6, i7, i8, 0, 0, 0, 0, 0, 0, 1, 0)) 100 | ); 101 | } 102 | } 103 | 104 | function main() 105 | { 106 | var v1 = 0; var v2 = 0; var v3 = 0; var v4 = 0; var v5 = 0; var v6 = 0; var v7 = 0; var v8 = 0; 107 | var u = 0; 108 | while not(equal8(v1, v2, v3, v4, v5, v6, v7, v8, 0, 0, 0, 0, 1, 1, 1, 0)) { 109 | // to output strings multiple individual putc calls are needed 110 | u = putc(0, 1, 0, 0, 0, 1, 1, 0); // F 111 | u = putc(0, 1, 1, 0, 1, 0, 0, 1); // i 112 | u = putc(0, 1, 1, 0, 0, 0, 1, 0); // b 113 | u = putc(0, 0, 1, 0, 0, 0, 0, 0); // space 114 | u = puti8(v1, v2, v3, v4, v5, v6, v7, v8); 115 | u = putc(0, 0, 1, 0, 0, 0, 0, 0); // space 116 | u = putc(0, 0, 1, 1, 1, 1, 0, 1); // = 117 | u = putc(0, 0, 1, 0, 0, 0, 0, 0); // space 118 | u = puti8(fibonacci(v1, v2, v3, v4, v5, v6, v7, v8)); 119 | u = endl(); 120 | // increment 121 | v1, v2, v3, v4, v5, v6, v7, v8 = add8( 122 | v1, v2, v3, v4, v5, v6, v7, v8, 0, 0, 0, 0, 0, 0, 0, 1); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /info.rkt: -------------------------------------------------------------------------------- 1 | #lang setup/infotab 2 | 3 | (define name "nand-lang") 4 | 5 | (define blurb '("A quick implementation of Jellonator's Nandlang as a racket #lang")) 6 | (define primary-file "main.rkt") 7 | (define homepage "https://github.com/rand-1/nand-lang") 8 | 9 | (define version "0.1") 10 | 11 | (define required-core-version "6.1") 12 | 13 | (define deps '("base" "peg")) 14 | -------------------------------------------------------------------------------- /main.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (module reader racket 4 | (require syntax/strip-context) 5 | 6 | (require peg/peg) 7 | (require nand-lang/nand-parser) 8 | (require nand-lang/nand-transform) 9 | 10 | (provide (rename-out [literal-read read] 11 | [literal-read-syntax read-syntax])) 12 | 13 | (define (nand-lang->scheme in) 14 | (nand->scheme (car (peg (and program (! (any-char))) (port->string in))))) 15 | 16 | (define (literal-read in) 17 | (syntax->datum 18 | (literal-read-syntax #f in))) 19 | 20 | (define (literal-read-syntax src in) 21 | (with-syntax ([body (nand-lang->scheme in)]) 22 | (strip-context 23 | #'(module anything racket 24 | (provide (all-defined-out)) 25 | (require nand-lang/nand-runtime) 26 | body 27 | (main '()))))) 28 | 29 | ) 30 | -------------------------------------------------------------------------------- /nand lang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rain-1/nand-lang/959701db0f44ab2138366d82c28bdb5c94d6a712/nand lang.png -------------------------------------------------------------------------------- /nand-parser.rkt: -------------------------------------------------------------------------------- 1 | #lang peg 2 | 3 | comment <- '//' [^\n]* ; 4 | _ < (comment / [ \t\n])* ; 5 | __ < (comment / [ \t\n]+) ; 6 | name <-- [a-zA-z][a-zA-Z0-9_]* _ ; 7 | 8 | OPEN < '(' _ ; 9 | CLOSE < ')' _ ; 10 | OPENBRACE < '{' _ ; 11 | CLOSEBRACE < '}' _ ; 12 | FUNCTION < 'function' __ ; 13 | COLON < ':' _ ; 14 | SEMICOLON < ';' _ ; 15 | ASSIGN < '=' _ ; 16 | NAND_ < '!' _ ; 17 | NAND <-- NAND_ ; 18 | COMMA < ',' _ ; 19 | VAR < 'var' __ ; 20 | IF < 'if' __ ; 21 | WHILE < 'while' __ ; 22 | 23 | program <-- (_ func)* !.; 24 | func <-- FUNCTION name (OPEN CLOSE / OPEN arglist CLOSE) funcbody; 25 | 26 | arglist <- arginputs? COLON argoutputs? ; 27 | arginputs <- args ; 28 | argoutputs <- args ; 29 | args <-- name (COMMA name)* ; 30 | 31 | funcbody <- OPENBRACE stmt* CLOSEBRACE ; 32 | 33 | stmt <- decl_stmt / if_stmt / while_stmt / assign_stmt ; 34 | assign_stmt <-- args ASSIGN expr SEMICOLON ; 35 | decl_stmt <-- VAR assign_stmt ; 36 | if_stmt <-- IF expr OPENBRACE stmt* CLOSEBRACE ; 37 | while_stmt <-- WHILE expr OPENBRACE stmt* CLOSEBRACE ; 38 | 39 | expr <-- expr_nonand (NAND expr_nonand)? ; 40 | expr_nonand <- const_expr / funcall_expr / var_expr ; 41 | const_expr <-- '0' / '1' ; 42 | var_expr <-- name ; 43 | funcall_expr <-- name OPEN exprs? CLOSE ; 44 | exprs <- expr (COMMA expr)* ; 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /nand-runtime.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide nand 4 | while 5 | puti8 6 | putc 7 | endl 8 | list->values) 9 | 10 | (define (nand args) 11 | (match args 12 | (`(0 0) '(1)) 13 | (`(1 0) '(1)) 14 | (`(0 1) '(1)) 15 | (`(1 1) '(0)))) 16 | 17 | (define (list->values lst) (apply values lst)) 18 | (define (single-value lst) (unless (= 1 (length lst)) (error 'value-not-single)) (car lst)) 19 | 20 | (define-syntax while 21 | (syntax-rules () 22 | ((while ...) 23 | (let loop () 24 | (when (= 1 (single-value )) 25 | ... (loop)) 26 | '())))) 27 | 28 | (define (binary->number lst) 29 | (foldr (lambda (x y) (+ (* y 2) x)) 0 lst)) 30 | 31 | (define (puti8 args) 32 | (apply (lambda (i1 i2 i3 i4 i5 i6 i7 i8) 33 | (display (binary->number (reverse (list i1 i2 i3 i4 i5 i6 i7 i8)))) 34 | '(0)) 35 | args)) 36 | 37 | (define (putc args) 38 | (apply (lambda (i1 i2 i3 i4 i5 i6 i7 i8) 39 | (display (integer->char (binary->number (reverse (list i1 i2 i3 i4 i5 i6 i7 i8))))) 40 | '(0)) 41 | args)) 42 | 43 | (define (endl args) (newline) '(0)) 44 | -------------------------------------------------------------------------------- /nand-transform.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require peg/peg) 4 | (require "nand-parser.rkt") 5 | 6 | (provide nand->scheme) 7 | 8 | (define (print p) (pretty-print p) (newline)) 9 | 10 | (define (nand->scheme prg) 11 | (match prg 12 | (`(program . ,funcs) 13 | `(begin . ,(map nand->scheme:func funcs))))) 14 | (define (nand->scheme:func f) 15 | (match f 16 | (`(func (name . ,nm) (args . ,args-in) (args . ,args-out) . ,body) 17 | (let* ((outs (map nand->scheme:name args-out)) 18 | (out-binds (map (lambda (v) (list v 0)) outs))) 19 | `(define (,(string->symbol nm) args) 20 | (apply (lambda ,(map nand->scheme:name args-in) 21 | (let ,out-binds 22 | . ,(append (map nand->scheme:stmt body) 23 | (list `(list . ,outs))))) 24 | args)))) 25 | (`(func (name . ,nm) . ,body) 26 | (nand->scheme:func `(func (name . ,nm) (args . ()) (args . ()) . ,body))))) 27 | (define (nand->scheme:stmt stmt) 28 | (match stmt 29 | (`(assign_stmt (args . ,args) (expr . ,nexpr)) 30 | `(set!-values ,(map nand->scheme:name args) (list->values ,(nand->scheme:nand-expr nexpr)))) 31 | (`(decl_stmt (assign_stmt (args . ,args) (expr . ,nexpr))) 32 | `(define-values ,(map nand->scheme:name args) (list->values ,(nand->scheme:nand-expr nexpr)))) 33 | (`(if_stmt ,exp . ,body) 34 | `(when (equal? '(1) ,(nand->scheme:exp exp)) 35 | . ,(map nand->scheme:stmt body))) 36 | (`(while_stmt ,exp . ,body) 37 | `(while ,(nand->scheme:exp exp) 38 | . ,(map nand->scheme:stmt body))) 39 | (else `(error 'unknown ',stmt)))) 40 | 41 | (define (nand->scheme:name nm) 42 | (match nm 43 | (`(name . ,e) 44 | (string->symbol e)))) 45 | 46 | (define (nand->scheme:nand-expr nexpr) 47 | (match nexpr 48 | (`(,a (NAND) . ,as) 49 | `(nand (append ,(nand->scheme:exp a) ,(nand->scheme:nand-expr as)))) 50 | (`(,a) 51 | (nand->scheme:exp a)))) 52 | 53 | (define (nand->scheme:exp e) 54 | (match e 55 | (`(expr . ,nexpr) 56 | (nand->scheme:nand-expr nexpr)) 57 | (`(var_expr name . ,nm) 58 | `(list ,(string->symbol nm))) 59 | (`(const_expr . "0") 60 | `(list 0)) 61 | (`(const_expr . "1") 62 | `(list 1)) 63 | (`(funcall_expr (name . ,f) . ,args) 64 | `(,(string->symbol f) (append . ,(map nand->scheme:exp args)))))) 65 | 66 | ;(print (nand->scheme (peg program t1))) 67 | --------------------------------------------------------------------------------