1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

1101
crypto/fift/lib/Asm.fif Normal file

File diff suppressed because it is too large Load diff

83
crypto/fift/lib/Fift.fif Normal file
View file

@ -0,0 +1,83 @@
{ 0 word drop 0 'nop } :: //
{ char " word 1 { swap { abort } if drop } } ::_ abort"
{ { bl word dup "" $= abort"comment extends after end of file" "*/" $= } until 0 'nop } :: /*
// { bl word 1 2 ' (create) } "::" 1 (create)
// { bl word 0 2 ' (create) } :: :
// { bl word 2 2 ' (create) } :: :_
// { bl word 3 2 ' (create) } :: ::_
// { bl word 0 (create) } : create
// { bl word (forget) } : forget
{ bl word 1 ' (forget) } :: [forget]
{ char " word 1 ' type } ::_ ."
{ swap ({) over 2+ -roll swap (compile) (}) } : does
{ 1 'nop does create } : constant
{ 2 'nop does create } : 2constant
{ hole constant } : variable
10 constant ten
{ bl word 1 { find 0= abort"word not found" } } :: (')
{ bl word find not abort"-?" 0 swap } :: [compile]
{ bl word 1 {
dup find { " -?" $+ abort } ifnot nip execute
} } :: @'
{ bl word 1 { swap 1 'nop does swap 0 (create) }
} :: =:
{ bl word 1 { -rot 2 'nop does swap 0 (create) }
} :: 2=:
{ <b swap s, b> } : s>c
{ s>c hash } : shash
// to be more efficiently re-implemented in C++ in the future
{ dup 0< ' negate if } : abs
{ 2dup > ' swap if } : minmax
{ minmax drop } : min
{ minmax nip } : max
"" constant <#
' $reverse : #>
{ swap 10 /mod char 0 + rot swap hold } : #
{ { # over 0<= } until } : #s
{ 0< { char - hold } if } : sign
// { dup abs <# #s rot sign #> nip } : (.)
// { (.) type } : ._
// { ._ space } : .
{ bl (-trailing) } : -trailing
{ char 0 (-trailing) } : -trailing0
{ char " word 1 ' $+ } ::_ +"
{ find 0<> dup ' nip if } : (def?)
{ bl word 1 ' (def?) } :: def?
{ bl word 1 { (def?) not } } :: undef?
{ def? ' skip-to-eof if } : skip-ifdef
{ bl word dup (def?) { drop skip-to-eof } { 'nop swap 0 (create) } cond } : library
{ bl word dup (def?) { 2drop skip-to-eof } { swap 1 'nop does swap 0 (create) } cond } : library-version
{ char ) word "$" swap $+ 1 { find 0= abort"undefined parameter" execute } } ::_ $(
// b s -- ?
{ sbitrefs rot brembitrefs rot >= -rot <= and } : s-fits?
{ 0 swap ! } : 0!
{ tuck @ + swap ! } : +!
{ tuck @ swap - swap ! } : -!
{ 1 swap +! } : 1+!
{ -1 swap +! } : 1-!
{ null swap ! } : null!
0 tuple constant nil
{ 1 tuple } : single
{ 2 tuple } : pair
{ 3 tuple } : triple
{ 1 untuple } : unsingle
{ 2 untuple } : unpair
{ 3 untuple } : untriple
{ over tuple? { swap count = } { 2drop false } cond } : tuple-len?
{ 0 tuple-len? } : nil?
{ 1 tuple-len? } : single?
{ 2 tuple-len? } : pair?
{ 3 tuple-len? } : triple?
{ 0 [] } : first
{ 1 [] } : second
{ 2 [] } : third
' pair : cons
' unpair : uncons
{ 0 [] } : car
{ 1 [] } : cdr
{ cdr car } : cadr
{ cdr cdr } : cddr
{ cdr cdr car } : caddr
{ null ' cons rot times } : list
{ true (atom) drop } : atom
{ bl word atom 1 'nop } ::_ `

436
crypto/fift/lib/Lisp.fif Normal file
View file

@ -0,0 +1,436 @@
library Lisp // tiny Lisp (or rather Scheme) interpreter
"Lists.fif" include
variable lisp-dict
{ hole dup 1 { @ execute } does create } : recursive
{ atom>$ +" undefined" abort } : report-not-found
// a l -- d -1 or a 0 Look up definition d of atom a in dictionary l
{ { dup null? { drop false true }
{ uncons -rot unpair -rot over eq?
{ drop nip true true } { nip swap false } cond
} cond
} until
} : lookup-in
// a dict -- def
{ lookup-in ' report-not-found ifnot } : lookup-or-fail
{ lisp-dict @ lookup-or-fail } : lisp-dict-lookup
// a d -- Defines a with definition d in dictionary lisp-dict
{ pair lisp-dict @ cons lisp-dict ! } : lisp-dict-int-define
{ box lisp-dict-int-define } : lisp-dict-define
// a d -- Defines new a with defininition d
{ over lisp-dict @ lookup-in { 2drop atom>$ +" already defined" abort }
{ drop lisp-dict-int-define } cond
} : lisp-dict-int-define-new
{ box lisp-dict-int-define-new } : lisp-dict-define-new
// a e -- Defines a with executable definition given by e
{ single lisp-dict-define-new } : lisp-dict-define-exec
// expr ctx def -- val
{ dup first execute } : run-definition
// expr ctx -- val
recursive lisp-ctx-eval {
over tuple?
{ over first over lisp-ctx-eval run-definition }
{ over atom? { lookup-or-fail @ } { drop } cond }
cond
} swap !
// exp -- value
{ lisp-dict @ lisp-ctx-eval } : lisp-eval
// (exprs) ctx -- (vals)
recursive lisp-ctx-eval-list
{ over null? { drop } {
swap uncons -rot over lisp-ctx-eval -rot lisp-ctx-eval-list cons
} cond
} swap !
// (exprs) ctx -- val
{ null rot {
dup null? { drop nip true } {
nip uncons swap 2 pick lisp-ctx-eval swap false
} cond } until
} : lisp-ctx-eval-list-last
// l c -- (args)
{ swap uncons nip swap lisp-ctx-eval-list } : extract-eval-arg-list
{ drop uncons nip } : extract-arg-list
// (x1 .. xn) e n -- x1 .. xn e
{ { swap uncons rot } swap times
swap null? not abort"invalid number of arguments"
} : unpack-list
// l c n e -- v
{ swap 2swap extract-eval-arg-list // e n (args)
-rot unpack-list execute
} : eval-exec-fixed
// l c n e -- v
{ 2 pick pair
swap 2swap extract-arg-list // [e c] n (args)
-rot unpack-list unpair swap execute
} : exec-fixed
// l c e -- v
{ -rot extract-eval-arg-list // e (args)
swap execute
} : eval-exec-list
{ -rot tuck extract-arg-list // e c (args)
swap rot execute
} : exec-list
// e a n --
{ rot 2 { // expr ctx def n e
rot drop eval-exec-fixed } does
lisp-dict-define-exec
} : lisp-fixed-primitive
{ rot 2 { rot drop exec-fixed } does lisp-dict-define-exec
} : lisp-fixed-lazy-primitive
// e a --
{ swap 1 { nip eval-exec-list } does lisp-dict-define-exec
} : lisp-primitive
{ swap 1 { nip exec-list } does lisp-dict-define-exec
} : lisp-lazy-primitive
// Uncomment next line for Fift booleans
// false constant #f true constant #t null constant no-answer
// Uncomment next line for Scheme booleans
`#f constant #f `#t constant #t #f constant no-answer
{ #f eq? } : lisp-false?
{ lisp-false? 0= } : lisp-true?
{ ' #t ' #f cond } : lisp-bool
// temp for defining a lot of primitives
{ bl word atom lisp-primitive } : L:
{ bl word atom swap lisp-dict-define } : L=:
{ bl word atom swap lisp-fixed-primitive } : #L:
{ 0 #L: } : 0L:
{ 1 #L: } : 1L:
{ 2 #L: } : 2L:
// basic primitives
{ sum-list } L: +
{ - } 2L: -
{ dup null? { drop 1 } { ' * foldl-ne } cond } L: *
{ / } 2L: /
{ mod } 2L: modulo
{ abs } 1L: abs
{ ' min foldl-ne } L: min
{ ' max foldl-ne } L: max
{ true ' and foldl } L: integer-and
{ false ' or foldl } L: integer-or
{ 0 ' xor foldl } L: integer-xor
{ not } 1L: integer-not
{ = lisp-bool } 2L: =
{ <> lisp-bool } 2L: <>
{ < lisp-bool } 2L: <
{ <= lisp-bool } 2L: <=
{ > lisp-bool } 2L: >
{ >= lisp-bool } 2L: >=
{ eq? lisp-bool } 2L: eq?
{ eqv? lisp-bool } 2L: eqv?
{ equal? lisp-bool } 2L: equal?
{ cons } 2L: cons
{ car } 1L: car
{ cdr } 1L: cdr
{ cadr } 1L: cadr
{ cddr } 1L: cddr
{ caddr } 1L: caddr
{ cdr cddr } 1L: cdddr
{ concat-list-lists } L: append
{ list-reverse } 1L: reverse
{ list-tail } 2L: list-tail
{ list-ref } 2L: list-ref
{ list-member-eq } 2L: memq
{ list-member-eqv } 2L: memv
{ list-member-equal } 2L: member
{ assq ' #f ifnot } 2L: assq
{ assv ' #f ifnot } 2L: assv
{ assoc ' #f ifnot } 2L: assoc
{ list? lisp-bool } 1L: list?
{ pair? lisp-bool } 1L: pair?
{ tuple? lisp-bool } 1L: tuple?
{ string? lisp-bool } 1L: string?
{ integer? lisp-bool } 1L: integer?
{ integer? lisp-bool } 1L: number?
{ count } 1L: width
{ list-length } 1L: length
{ [] } 2L: tuple-ref
{ first } 1L: first
{ second } 1L: second
{ third } 1L: third
{ 3 [] } 1L: fourth
{ list>tuple } 1L: list->tuple
{ explode list } 1L: tuple->list
null L=: null
{ atom? lisp-bool } 1L: symbol?
{ atom } 1L: string->symbol
{ atom>$ } 1L: symbol->string
{ dup #f eq? swap #t eq? or lisp-bool } 1L: boolean?
#t L=: else
#f L=: #f
#t L=: #t
{ null? lisp-bool } 1L: null?
{ 0= lisp-bool } 1L: zero?
{ 0> lisp-bool } 1L: positive?
{ 0< lisp-bool } 1L: negative?
{ 1 and 0= lisp-bool } 1L: even?
{ 1 and 0<> lisp-bool } 1L: odd?
{ bye } 0L: exit
{ .l null } 1L: write
{ lisp-eval } 1L: eval
{ drop } `quote 1 lisp-fixed-lazy-primitive
'nop L: list
{ list>tuple } L: tuple
{ list-last } L: begin
{ $len } 1L: string-length
{ concat-string-list } L: string-append
{ $= lisp-bool } 2L: string=?
{ $cmp 0< lisp-bool } 2L: string<?
{ $cmp 0<= lisp-bool } 2L: string<=?
{ $cmp 0> lisp-bool } 2L: string>?
{ $cmp 0>= lisp-bool } 2L: string>=?
{ (number) dup 1 = { drop } { ' 2drop if no-answer } cond
} 1L: string->number
{ (.) } 1L: number->string
{ box? lisp-bool } 1L: box?
{ box } 1L: box
{ hole } 0L: new-box
{ @ } 1L: unbox
{ tuck swap ! } 2L: set-box!
{ abort } 1L: error
{ dup find { nip execute } { +" -?" abort } cond } : find-execute
{ explode-list 1- roll find-execute } L: fift-exec
{ explode-list dup 1- swap roll find-execute } L: fift-exec-cnt
{ uncons swap find-execute } L: fift-exec-list
// end of basic primitives
forget L: forget #L: forget L=:
forget 0L: forget 1L: forget 2L:
{ { dup tuple? ' do-quote if } list-map } : map-quote
{ uncons ' cons foldr-ne map-quote
null swap cons lisp-dict @ rot run-definition
} `apply lisp-primitive // bad: should have preserved original context
// e1 e2 e3 ctx
{ 3 exch 3 pick lisp-ctx-eval lisp-true? ' swap if nip swap lisp-ctx-eval }
`if 3 lisp-fixed-lazy-primitive
// (e) ctx
{ #t -rot
{ over null? { 2drop true } {
swap uncons swap 2 pick lisp-ctx-eval dup lisp-true? // v' c t v ?
{ swap 2swap nip false } { -rot 2drop nip true } cond
} cond } until
} `and lisp-lazy-primitive
{ #f -rot
{ over null? { 2drop true } {
swap uncons swap 2 pick lisp-ctx-eval dup lisp-false? // v' c t v ?
{ swap 2swap nip false } { -rot 2drop nip true } cond
} cond } until
} `or lisp-lazy-primitive
{ lisp-false? lisp-bool } `not 1 lisp-fixed-primitive
// cond-clause ctx -- v -1 or 0
{ swap uncons -rot dup `else eq? {
drop lisp-ctx-eval-list-last true } {
over lisp-ctx-eval lisp-true? {
lisp-ctx-eval-list-last true } {
2drop false
} cond } cond
} : eval-cond-clause
// (clauses) ctx -- v
{ { over null? { no-answer true } {
swap uncons -rot over eval-cond-clause } cond
} until -rot 2drop
} `cond lisp-lazy-primitive
{ lisp-dict @ lookup-in { hole tuck lisp-dict-int-define } ifnot
} : lisp-create-global-var
// a e ctx -- old (simple) define
{ drop over atom? not abort"only a variable can be define'd"
over lisp-create-global-var swap lisp-eval swap !
} drop // `define 2 lisp-fixed-lazy-primitive
{ tuck lisp-ctx-eval rot dup atom? not abort"only a variable can be set"
rot lookup-or-fail dup @ -rot !
} `set! 2 lisp-fixed-lazy-primitive
// define lambda
{ { dup null? { drop true true }
{ uncons swap atom? { false } { drop false true } cond } cond
} until
} : var-list?
{ { dup null? over atom? or { drop true true }
{ uncons swap atom? { false } { drop false true } cond } cond
} until
} : lambda-var-list?
// (quote x) -- x -1 ; else 0
{ dup pair? { uncons swap `quote eq? { car true } { drop false } cond }
{ drop false } cond
} : is-quote?
recursive match-arg-list-acc
// l (vars) (args) -- ((var . arg) ...)+l -1 or ? 0
{ over atom? { over `_ eq? { 2drop } { pair swap cons } cond true } {
over null? { nip null? } { // (vars) (args)
over tuple? not { 2drop false } {
over is-quote? { eq? nip } { // (v) (a)
dup tuple? not { 2drop false } {
over count over count over <> { drop 2drop false } { // l [v] [a] n
3 roll 0 rot { // [v] [a] l i
dup 0< {
3 pick over [] swap // [v] [a] l vi i
3 pick over [] 2swap rot // [v] [a] i l vi ai
match-arg-list-acc { // [v] [a] i l'
swap 1+ } { nip -1 } cond
} ifnot
} swap times
2swap 2drop 0>=
} cond } cond } cond } cond } cond } cond
} swap !
{ null -rot match-arg-list-acc } : match-arg-list
// ((var . arg)...) ctx -- ctx'
{ { over null? not }
{ swap uncons swap unpair box pair rot cons } while
nip
} : extend-ctx-by-list
// ((vars) body) ctx
{ swap uncons -rot
dup lambda-var-list? not abort"invalid formal parameter list"
{ // l-expr ctx' [_ body ctx (vars)]
-rot 2 pick 3 [] swap rot // [_ body ...] (vars) ctx' l-expr
uncons nip swap lisp-ctx-eval-list // [_ body ...] (vars) (arg-vals)
match-arg-list not abort"invalid arguments to lambda" // [_ body ...] ((var arg)...)
over third extend-ctx-by-list // [_ body ctx (vars)] ctx''
swap second swap lisp-ctx-eval-list-last
} 3 -roll 4 tuple
} : make-lambda
{ make-lambda } `lambda lisp-lazy-primitive
// (a e) ctx -- more sophisticated (define a e)
{ drop uncons swap dup atom? { // (e) a
tuck lisp-create-global-var
swap lisp-dict @ lisp-ctx-eval-list-last swap !
} { // (e) (a v..)
uncons over atom? not abort"only variables can be define'd" // (e) a (v..)
rot cons over lisp-create-global-var // a ((v..) (e)) h
swap lisp-dict @ make-lambda swap !
} cond
} `define lisp-lazy-primitive
// ((x e) ..) ctx -- ((x.v) ..)
recursive eval-assign-list
{ over null? { drop } {
swap uncons swap uncons // ctx t x (e)
over atom? not abort"invalid variable name in assignment list"
3 pick lisp-ctx-eval-list-last // ctx t x v
pair swap rot eval-assign-list cons
} cond
} swap !
// (((x v) ..) body) ctx -- let construct
{ swap uncons swap 2 pick eval-assign-list // ctx body ((x v)...)
rot extend-ctx-by-list lisp-ctx-eval-list-last
} `let lisp-lazy-primitive
// ((x e) ..) ctx -- ctx'
{ swap {
dup null? { drop true } {
uncons swap uncons // ctx t x (e)
over atom? not abort"invalid variable name in assignment list"
3 pick lisp-ctx-eval-list-last // ctx t x v
box pair rot cons swap false
} cond } until
} : compute-let*-ctx
// (((x v) ..) body) ctx -- let* construct
{ swap uncons swap rot compute-let*-ctx lisp-ctx-eval-list-last
} `let* lisp-lazy-primitive
// ((x e) ..) ctx -- ((h e) ..) ctx' , with x bound to h in ctx'
recursive prepare-letrec-ctx {
over null? {
swap uncons swap uncons swap // ctx t (e) x
hole tuck pair swap rot cons // ctx t (x.h) (h e)
3 -roll rot cons prepare-letrec-ctx // (h e) t ctx'
-rot cons swap
} ifnot
} swap !
// (((x v) ..) body) ctx -- letrec construct
{ swap uncons swap rot prepare-letrec-ctx swap { // body ctx' ((h e)..)
dup null? { drop true } {
uncons -rot uncons 2 pick lisp-ctx-eval-list-last // body t ctx' h v
swap ! swap false
} cond } until
lisp-ctx-eval-list-last
} `letrec lisp-lazy-primitive
// (e (p e)...) ctx -- match construct
{ swap uncons swap 2 pick lisp-ctx-eval swap { // ctx v ((p e)..)
dup null? { drop 2drop no-answer true } {
uncons swap uncons swap 3 pick // ctx v t e p v
match-arg-list { // ctx v t e ((x' . v')...)
2swap 2drop rot extend-ctx-by-list lisp-ctx-eval-list-last true } {
2drop false
} cond } cond } until
} `match lisp-lazy-primitive
//
lisp-dict @ constant original-lisp-dict
{ original-lisp-dict lisp-dict ! } : reset-lisp
{ ' drop { lisp-eval .l cr } List-generic( } :_ LISP-EVAL-PRINT(
// LISP-EVAL-PRINT((+ 3 4) (* 5 6)) computes and prints 12 and 30
{ hole dup 1 { @ nip } does swap
1 { swap lisp-eval swap ! } does
List-generic(
} :_ LISP-EVAL(
// LISP-EVAL((+ 3 4) (* 5 6)) computes 12 and 30, returns only 30
// /*
LISP-EVAL-PRINT(
(define succ (lambda (x) (+ x 1)))
(define (twice f) (lambda (x) (f (f x))))
(define (fact n) (if (= n 0) 1 (* n (fact (- n 1)))))
(fact ((twice succ) 5))
(define compare (lambda (x y) (cond ((< x y) 'less) ((= x y) 'equal) (else 'greater))))
(compare 2 3)
(compare 7 (+ 2 3))
(define next (let ((cnt 0)) (lambda () (set! cnt (+ cnt 1)) cnt)))
(list (next) (next))
(define new-counter (lambda () (let ((x 0)) (lambda () (set! x (+ x 1)) x))))
(define c1 (new-counter))
(define c2 (new-counter))
(list (c1) (c1) (c2) (c1) (c2) (c1) (c1) (c2) (c2))
(let* ((x (+ 2 3)) (y (* x x)) (z (+ x y))) (list x y z))
(letrec ((even? (lambda (n) (if (= n 0) #t (odd? (- n 1)))))
(odd? (lambda (n) (if (= n 0) #f (even? (- n 1))))))
(even? 88))
(define (len l) (if (null? l) 0 (+ 1 (len (cdr l)))))
(len '(2 3 9))
(define (len2 l) (match l (() 0) ((x . t) (+ 1 (len2 t)))))
(len2 '(2 3 9))
(define (foo x) (match x
(('zero) 0)
(('succ x) (+ (foo x) 1))
(('plus x y) (+ (foo x) (foo y)))
(('minus x y) (- (foo x) (foo y)))
(x x)))
(foo '(plus (succ (zero)) (minus (succ (succ 5)) 3)))
(define (bar x) (match x
(['zero] 0)
(['succ x] (+ (bar x) 1))
(['plus x y] (+ (bar x) (bar y)))
(['minus x y] (- (bar x) (bar y)))
(['const x] x)))
(bar '[plus [succ [zero]] [minus [succ [succ [const 5]]] [const 3]]])
(define (map f l) (letrec
((map-f (lambda (l) (match l
(() ())
((h . t) (cons (f h) (map-f t)))))))
(map-f l)))
(map (lambda (x) (* x (+ 2 x))) '(2 3 9))
(define (make-promise proc) (let ((result-ready? #f) (result #f))
(lambda ()
(if result-ready? result
(let ((x (proc)))
(if result-ready? result
(begin (set! result x) (set! result-ready? #t) result)))))))
(define (force promise) (promise))
)
// */
// words for invoking Lisp definitions from Fift
// (args) def -- val
{ null rot map-quote cons lisp-dict @ rot run-definition
} : invoke-lisp-definition
{ atom lisp-dict-lookup 1 { @ invoke-lisp-definition }
} : (invoke-lisp)
{ bl word (invoke-lisp) } :: invoke-lisp
// ( 2 3 ) invoke-lisp compare .l
{ atom lisp-dict-lookup 2 { @ mklist-1 invoke-lisp-definition }
} : (invoke-lisp-fixed)
{ bl word (invoke-lisp-fixed) } :: invoke-lisp-fixed
// 9 8 2 invoke-lisp-fixed compare .l
{ bl word (invoke-lisp) does } : make-lisp-invoker
{ bl word (invoke-lisp-fixed) does } : make-lisp-fixed-invoker
// 2 make-lisp-fixed-invoker compare : compare
// 3 9 compare
// import Lisp definitions as Fift words
{ bl word dup (invoke-lisp) does swap 0 (create) } : import-lisp
{ bl word tuck (invoke-lisp-fixed) does swap 0 (create) } : import-lisp-fixed
// 1 import-lisp-fixed fact
// 7 fact .

184
crypto/fift/lib/Lists.fif Normal file
View file

@ -0,0 +1,184 @@
library Lists // List utilities
//
{ hole dup 1 { @ execute } does create } : recursive
// x x' -- ? recursively compares two S-expressions
recursive equal? {
dup tuple? {
over tuple? {
over count over count over = { // t t' l ?
0 { dup 0>= { 2dup [] 3 pick 2 pick [] equal? { 1+ } { drop -1 } cond
} if } rot times
nip nip 0>=
} { drop 2drop false } cond
} { 2drop false } cond
} { eqv? } cond
} swap !
// (a1 .. an) -- (an .. a1)
{ null swap { dup null? not } { uncons swap rot cons swap } while drop } : list-reverse
// (a1 .. an) -- an Computes last element of non-empty list l
{ { uncons dup null? { drop true } { nip false } cond } until } : list-last
// l l' -- l++l' Concatenates two lists
recursive list+ {
over null? { nip } { swap uncons rot list+ cons } cond
} swap !
// l l' -- l'' -1 or 0, where l = l' ++ l''
// Removes prefix from list
{ { dup null? { drop true true } {
swap dup null? { 2drop false true } { // l' l
uncons swap rot uncons -rot equal? { false } {
2drop false true
} cond } cond } cond } until
} : list-
// (a1 .. an) -- a1 .. an n Explodes a list
{ 0 { over null? not } { swap uncons rot 1+ } while nip } : explode-list
// (a1 .. an) x -- a1 .. an n x Explodes a list under the topmost element
{ swap explode-list dup 1+ roll } : explode-list-1
// l -- t Transforms a list into a tuple with the same elements
{ explode-list tuple } : list>tuple
// a1 ... an n x -- (a1 .. an) x
{ null swap rot { -rot cons swap } swap times } : mklist-1
// (s1 ... sn) -- s1+...+sn Concatenates a list of strings
{ "" { over null? not } { swap uncons -rot $+ } while nip
} : concat-string-list
// (x1 ... xn) -- x1+...+xn Sums a list of integers
{ 0 { over null? not } { swap uncons -rot + } while nip
} : sum-list
// (a1 ... an) a e -- e(...e(e(a,a1),a2),...),an)
{ -rot { over null? not } { swap uncons -rot 3 pick execute } while nip nip
} : foldl
// (a1 ... an) e -- e(...e(e(a1,a2),a3),...),an)
{ swap uncons swap rot foldl } : foldl-ne
// (a1 ... an) a e -- e(a1,e(a2,...,e(an,a)...))
recursive foldr {
rot dup null? { 2drop } {
uncons -rot 2swap swap 3 pick foldr rot execute
} cond
} swap !
// (a1 ... an) e -- e(a1,e(a2,...,e(a[n-1],an)...))
recursive foldr-ne {
over cdr null? { drop car } {
swap uncons 2 pick foldr-ne rot execute
} cond
} swap !
// (l1 ... ln) -- l1++...++ln Concatenates a list of lists
{ dup null? { ' list+ foldr-ne } ifnot } : concat-list-lists
// (a1 .. an . t) n -- t Computes the n-th tail of a list
{ ' cdr swap times } : list-tail
// (a0 .. an ..) n -- an Computes the n-th element of a list
{ list-tail car } : list-ref
// l -- ?
{ { dup null? { drop true true } {
dup pair? { cdr false } {
drop false true
} cond } cond } until
} : list?
// l -- n
{ 0 { over null? not } { 1+ swap uncons nip swap } while nip
} : list-length
// l e -- t // returns tail of l after first member that satisfies e
{ swap {
dup null? { nip true } {
tuck car over execute { drop true } {
swap cdr false
} cond } cond } until
} : list-tail-from
// a l -- t // tail of l after first occurence of a using eq?
{ swap 1 ' eq? does list-tail-from } : list-member-eq
{ swap 1 ' eqv? does list-tail-from } : list-member-eqv
{ swap 1 ' equal? does list-tail-from } : list-member-equal
// a l -- ?
{ list-member-eq null? not } : list-member?
{ list-member-eqv null? not } : list-member-eqv?
// l -- a -1 or 0 // returns car l if l is non-empty
{ dup null? { drop false } { car true } cond
} : safe-car
{ dup null? { drop false } { car second true } cond
} : get-first-value
// l e -- v -1 or 0
{ list-tail-from safe-car } : assoc-gen
{ list-tail-from get-first-value } : assoc-gen-x
// a l -- (a.v) -1 or 0 -- returns first entry (a . v) in l
{ swap 1 { swap first eq? } does assoc-gen } : assq
{ swap 1 { swap first eqv? } does assoc-gen } : assv
{ swap 1 { swap first equal? } does assoc-gen } : assoc
// a l -- v -1 or 0 -- returns v from first entry (a . v) in l
{ swap 1 { swap first eq? } does assoc-gen-x } : assq-val
{ swap 1 { swap first eqv? } does assoc-gen-x } : assv-val
{ swap 1 { swap first equal? } does assoc-gen-x } : assoc-val
// (a1 .. an) e -- (e(a1) .. e(an))
recursive list-map {
over null? { drop } {
swap uncons -rot over execute -rot list-map cons
} cond
} swap !
//
// create Lisp-style lists using words "(" and ")"
//
variable ')
'nop box constant ',
{ ") without (" abort } ') !
{ ') @ execute } : )
anon constant dot-marker
// m x1 ... xn t m -- (x1 ... xn . t)
{ swap
{ -rot 2dup eq? not }
{ over dot-marker eq? abort"invalid dotted list"
swap rot cons } while 2drop
} : list-tail-until-marker
// m x1 ... xn m -- (x1 ... xn)
{ null swap list-tail-until-marker } : list-until-marker
{ over dot-marker eq? { nip 2dup eq? abort"invalid dotted list" }
{ null swap } cond
list-tail-until-marker
} : list-until-marker-ext
{ ') @ ', @ } : ops-get
{ ', ! ') ! } : ops-set
{ anon dup ops-get 3 { ops-set list-until-marker-ext } does ') ! 'nop ', !
} : (
// test of Lisp-style lists
// ( 42 ( `+ 9 ( `* 3 4 ) ) "test" ) .l cr
// ( `eq? ( `* 3 4 ) 3 4 * ) .l cr
// `alpha ( `beta `gamma `delta ) cons .l cr
// { ( `eq? ( `* 3 5 pick ) 3 4 roll * ) } : 3*sample
// 17 3*sample .l cr
// similar syntax _( x1 .. xn ) for tuples
{ 2 { 1+ 2dup pick eq? } until 3 - nip } : count-to-marker
{ count-to-marker tuple nip } : tuple-until-marker
{ anon dup ops-get 3 { ops-set tuple-until-marker } does ') ! 'nop ', ! } : _(
// test of tuples
// _( _( 2 "two" ) _( 3 "three" ) _( 4 "four" ) ) .dump cr
// pseudo-Lisp tokenizer
"()[]'" 34 hold constant lisp-delims
{ lisp-delims 11 (word) } : lisp-token
{ null cons `quote swap cons } : do-quote
{ 1 { ', @ 2 { 2 { ', ! execute ', @ execute } does ', ! }
does ', ! } does
} : postpone-prefix
{ ', @ 1 { ', ! } does ', ! } : postpone-',
( `( ' ( pair
`) ' ) pair
`[ ' _( pair
`] ' ) pair
`' ' do-quote postpone-prefix pair
`. ' dot-marker postpone-prefix pair
`" { char " word } pair
`;; { 0 word drop postpone-', } pair
) constant lisp-token-dict
variable eol
{ eol @ eol 0! anon dup ') @ 'nop 3
{ ops-set list-until-marker-ext true eol ! } does ') ! rot ', !
{ lisp-token dup (number) dup { roll drop } {
drop atom dup lisp-token-dict assq { nip second execute } if
} cond
', @ execute
eol @
} until
-rot eol ! execute
} :_ List-generic(
{ 'nop 'nop List-generic( } :_ LIST(
// LIST((lambda (x) (+ x 1)) (* 3 4))
// LIST('(+ 3 4))
// LIST(2 3 "test" . 9)
// LIST((process '[plus 3 4]))

266
crypto/fift/lib/Stack.fif Normal file
View file

@ -0,0 +1,266 @@
library Stack // advanced stack manupulation library
"Lists.fif" include
// S(a b c - a c 2 a b) would compile to code performing the requested stack manipulation
// interface to low-level stack manipulation primitives
{ (number) 1- abort"index expected" dup 0 < over 255 > or
abort"index 0..255 expected"
} : (idx)
// push(n) : a0 .. an - a0 .. an a0 equivalent to "n pick"
// push(0) = dup, push(1) = over
{ 0 char ) word (idx) <push> } ::_ push(
// pop(n) : a0 a1 .. a(n-1) an - an a1 .. a(n-1)
// pop(0) = drop, pop(1) = nip
{ 0 char ) word (idx) <pop> } ::_ pop(
// xchg(i,j) : equivalent to "i j exch2"
{ 0 char , word (idx) char ) word (idx) <xchg> } ::_ xchg(
// xchg0(i) : equivalent to "i exch" or "xchg(0,i)"
// xchg0(1) = swap
{ 0 char ) word (idx) 0 <xchg> } ::_ xchg0(
forget (idx)
// parser for stack notation expressions
")" 34 hold +" -" constant stk-delims
anon constant stk-start
anon constant stk-to
variable stk-mode
{ stk-delims 11 (word) } : stk-token
'nop : mk-lit
// stk-start vn ... v0 -- stk-start ... v0 i where v[i]=v0
{ 0 {
1+ 2dup 2+ pick dup stk-start eq? { 2drop drop 0 true } { eqv? } cond
} until
} : stk-lookup
// stk-start a1 .. an stk-to b1 .. bm -- [a1 .. an] [b1 .. bm]
{ stk-mode @ 0= abort"identifier expected" } : chk-lit
{ stk-to list-until-marker stk-mode !
stk-start list-until-marker stk-mode @
stk-mode 0!
} : build-stk-effect
{ stk-start stk-mode 0! {
stk-token dup ")" $= { drop true } {
dup "-" $= {
drop stk-mode @ abort"duplicate -" true stk-mode ! stk-to false } {
dup 34 chr $= { chk-lit drop char " word mk-lit false } {
dup (number) ?dup { chk-lit 1- { swap mk-lit -rot } if mk-lit nip false } {
atom dup `_ eq? { stk-mode @ abort"identifier expected" false } {
stk-lookup 0= stk-mode @ = {
stk-mode @ { atom>$ +" -?" } { atom>$ +" redefined" } cond abort } {
false
} cond } cond } cond } cond } cond } cond } until
stk-mode @ 0= abort"'-' expected"
build-stk-effect
} :_ parse-stk-list(
// stack operation list construction
variable op-rlist
{ op-rlist null! } : clear-op-list
{ op-rlist @ list-reverse } : get-op-list
{ op-rlist @ cons op-rlist ! } : issue-op
{ minmax `xchg -rot triple } : op-xchg
{ `push swap pair } : op-push
{ `lit swap pair } : op-lit
{ `pop swap pair } : op-pop
0 op-pop constant op-drop
{ 2dup <> { op-xchg issue-op } if } : issue-xchg
{ op-push issue-op } : issue-push
{ op-lit issue-op } : issue-lit
{ op-pop issue-op } : issue-pop
{ op-drop issue-op } : issue-drop
{ ' issue-drop swap times } : issue-drop-#
// emulated stack contents
variable emul-stk
{ emul-stk @ count } : emul-depth
{ emul-depth 1- swap - } : adj-i
{ emul-depth 1- tuck swap - swap rot - swap } : adj-ij
// i j --
{ adj-ij 2dup emul-stk @ tuck swap [] swap rot [] rot // i sj si j
emul-stk @ -rot []= swap rot []= emul-stk !
} : emul-xchg
{ emul-stk @ tpop drop emul-stk ! } : emul-drop
// i --
{ 0 emul-xchg emul-drop } : emul-pop
// i -- s[i]
{ emul-stk @ swap [] } : emul-stk[]
// i -- si
{ adj-i emul-stk[] } : emul-get
{ 0 emul-get } : emul-tos
// v i -- ? Check whether s[i]=v
{ dup emul-depth < { emul-stk[] eqv? } { 2drop false } cond } : emul[]-eq?
// v -- i or -1 Returns maximum i with s[i]=v
{ emul-stk @ dup count { // v s i
?dup 0= { -1 true } { 1- 2dup [] 3 pick eqv? } cond // v s i' ?
} until nip nip
} : emul-stk-lookup-rev
// i --
{ emul-get emul-stk @ swap , emul-stk ! } : emul-push
{ emul-stk @ swap , emul-stk ! } : emul-lit
// show emulated stack contents similarly to .s
{ emul-stk @ explode dup 1 reverse ' .l swap times cr } : .e
// both issue an operation and emulate it
{ 2dup issue-xchg emul-xchg } : issue-emul-xchg
{ dup issue-push emul-push } : issue-emul-push
{ dup issue-lit emul-lit } : issue-emul-lit
{ dup issue-pop emul-pop } : issue-emul-pop
{ issue-drop emul-drop } : issue-emul-drop
{ ' issue-emul-drop swap times } : issue-emul-drop-#
// b.. s -- b.. s moves tos value to stk[s]
{ dup emul-stk[] 2 pick cdr list-member-eqv? {
dup adj-i 0 issue-emul-xchg } { dup adj-i issue-emul-pop } cond
} : move-tos-to
// new s -- ops registered
{ { over null? not } {
// .sl .e get-op-list .l cr
// get-op-list list-length 100 > abort"too long"
emul-depth over >
{ over emul-tos swap list-member-eqv? not } { false } cond {
// b.. s tos unneeded
issue-emul-drop } {
over car // b.. s b1
2dup swap emul[]-eq? { drop swap cdr swap 1+ } {
dup emul-stk-lookup-rev // b.. s b1 i
dup 0< { // b.. s b1 i not found, must be a literal
drop dup atom? abort"unavailable value"
issue-emul-lit } {
dup 3 pick < { // b.. s b1 i found in bottom s stack values
nip adj-i issue-emul-push // b.. s
dup emul-depth 1- < { move-tos-to } if
} {
emul-depth 1- over = { // b.. s b1 i found in tos
2drop move-tos-to
} { // b.. s b1 i
nip over adj-ij issue-emul-xchg
} cond } cond } cond } cond } cond } while
nip emul-depth swap - issue-emul-drop-#
} : generate-reorder-ops
// old new -- op-list
{ emul-stk @ op-rlist @ 2swap
swap list>tuple emul-stk ! clear-op-list
0 generate-reorder-ops get-op-list
-rot op-rlist ! emul-stk !
} : generate-reorder
{ parse-stk-list( generate-reorder } :_ SG(
// op-list rewriting according to a ruleset
// l f l1 l2 -- l' -1 or l f with l' = l2 + (l - l1)
{ push(3) rot list- { list+ nip nip true } { drop } cond
} : try-rule
// l f ll -- l' -1 or l f
{ { dup null? not } { uncons 3 -roll unpair try-rule rot } while drop
} : try-ruleset
// l ll -- l'
{ swap { over false swap try-ruleset 0= } until nip
} : try-ruleset*
// l ruleset -- l'
recursive try-ruleset*-everywhere {
tuck try-ruleset* dup null? { nip } {
uncons rot try-ruleset*-everywhere cons } cond
} swap !
LIST(
[([xchg 0 1] [xchg 0 2]) ([rot])]
[([xchg 0 1] [xchg 1 2]) ([-rot])]
[([xchg 0 2] [xchg 1 2]) ([rot])]
[([xchg 0 2] [xchg 0 1]) ([-rot])]
[([xchg 1 2] [xchg 0 1]) ([rot])]
[([xchg 1 2] [xchg 0 2]) ([-rot])]
[([xchg 0 1] [rot]) ([xchg 0 2])]
[([-rot] [xchg 0 1]) ([xchg 0 2])]
[([xchg 0 2] [xchg 1 3]) ([2swap])]
[([xchg 1 3] [xchg 0 2]) ([2swap])]
[([push 1] [push 1]) ([2dup])]
[([push 3] [push 3]) ([2over])]
[([pop 0] [pop 0]) ([2drop])]
[([pop 1] [pop 0]) ([2drop])]
[([xchg 0 1] [push 1]) ([tuck])]
[([rot] [-rot]) ()]
[([-rot] [rot]) ()]
) constant fift-stack-ruleset
{ fift-stack-ruleset try-ruleset*-everywhere } : fift-ops-rewrite
{ SG( fift-ops-rewrite } :_ SGF(
// helpers for creating Fift source strings for one fift-op
// i j -- s
{ minmax over { "xchg(" rot (.) $+ +"," swap (.) $+ +")" }
{ nip dup 1 = { drop "swap" } {
?dup { "xchg0(" swap (.) $+ +")" } { "" } cond
} cond } cond
} : source-<xchg>
// i -- s
{ dup 1 = { drop "over" } {
?dup { "push(" swap (.) $+ +")" } { "dup" } cond
} cond
} : source-<push>
// i -- s
{ dup 1 = { drop "nip" } {
?dup { "pop(" swap (.) $+ +")" } { "drop" } cond
} cond
} : source-<pop>
// lit -- s
{ dup string? { char " chr swap $+ char " hold } { (.) } cond
} : source-<lit>
// dictionary with all fift op compilation/source creation
{ 0 swap (compile) } : fop-compile
( _( `xchg 2 { <xchg> fop-compile } { source-<xchg> swap cons } )
_( `push 1 { <push> fop-compile } { source-<push> swap cons } )
_( `pop 1 { <pop> fop-compile } { source-<pop> swap cons } )
_( `lit 1 { 1 'nop (compile) } { source-<lit> swap cons } )
_( `rot 0 { ' rot fop-compile } { "rot" swap cons } )
_( `-rot 0 { ' -rot fop-compile } { "-rot" swap cons } )
_( `tuck 0 { ' tuck fop-compile } { "tuck" swap cons } )
_( `2swap 0 { ' 2swap fop-compile } { "2swap" swap cons } )
_( `2drop 0 { ' 2drop fop-compile } { "2drop" swap cons } )
_( `2dup 0 { ' 2dup fop-compile } { "2dup" swap cons } )
_( `2over 0 { ' 2over fop-compile } { "2over" swap cons } )
) box constant fift-op-dict
{ dup atom? { atom>$ } { drop "" } cond
"unknown operation " swap $+ abort
} : report-unknown-op
variable 'fop-entry-exec
// process fift-op according to 'fop-entry-exec
// ... op - ...
{ dup first dup fift-op-dict @ assq { report-unknown-op } ifnot
dup second 1+ push(3) count <> abort"incorrect param count"
nip swap explode dup roll drop 1- roll // o2 .. on entry
'fop-entry-exec @ execute
} : process-fift-op
// compile op-list into Fift wordlist
// wl op-list -- wl'
{ { third execute } 'fop-entry-exec !
swap ' process-fift-op foldl } : compile-fift-op*
// op-list -- e
{ fift-ops-rewrite ({) swap compile-fift-op* (}) } : ops>wdef
// S(<orig-stack> - <new-stack>) compiles a "word" performing required action
{ SG( ops>wdef 0 swap } ::_ S(
// 1 2 3 S(a b c - c a b a) .s would print 3 1 2 1
// transform op-list into Fift source
// ls op -- ls'
{ fift-ops-rewrite
{ 3 [] execute } 'fop-entry-exec !
null ' process-fift-op foldl
dup null? { drop "" } { { +" " swap $+ } foldr-ne } cond
} : ops>$
{ SG( ops>$ 1 'nop } ::_ $S(
{ SG( ops>$ type } :_ .$S(
// $S(a b c - b c a c a c) => string "rot 2dup over"
// S(a b c - b c a c a c) => compile/execute block { rot 2dup over }
// $S(_ x y _ - y x) => string "drop pop(2)"
// .$S(x1 x2 - 17 x1) => print string "drop 17 swap"
// simplify/transform sequences of stack manipulation operations
LIST(. [a b c d e f g h i j]) constant std-stack
{ stk-start std-stack explode drop stk-to std-stack explode drop
} : simplify<{
{ build-stk-effect generate-reorder ops>$ } : }>stack
// simplify<{ drop drop over over -13 }>stack => string "2drop 2dup -13"
// simplify<{ 17 rot }>stack => string "swap 17 swap"
// simplify<{ 5 1 reverse }>stack => string "xchg(1,5) xchg(2,4)"

125
crypto/fift/lib/TonUtil.fif Normal file
View file

@ -0,0 +1,125 @@
library TonUtil // TON Blockchain Fift Library
"Lists.fif" include
-1 constant Masterchain
0 constant Basechain
// parse workchain id
// ( S -- workchain )
{ (number) 1- abort"workchain id must be an integer"
dup 32 fits not abort"workchain id must fit in 32 bits"
} : parse-workchain-id
{ (number) 1- abort"integer expected" } : parse-int
// Private key load/generate
// ( fname -- pubkey privkey )
{ dup ."Loading private key from file " type cr
file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
dup priv>pub swap
} : load-keypair
// ( fname -- pubkey privkey )
{ dup file-exists?
{ load-keypair }
{ dup newkeypair swap rot over swap B>file
rot ."Saved new private key to file " type cr
} cond
} : load-generate-keypair
// Parse smart-contract address
// ( S -- workchain addr bounce? )
{ $>smca not abort"invalid smart-contract address"
1 and 0=
} : parse-smc-addr
// ( wc addr -- ) Show address in <workchain>:<account> form
{ swap ._ .":" x. } : .addr
// ( wc addr flags -- ) Show address in base64url form
{ smca>$ type } : .Addr
// ( wc addr fname -- ) Save address to file in 36-byte format
{ -rot 256 u>B swap 32 i>B B+ swap B>file } : save-address
// ( wc addr fname -- ) Save address and print message
{ dup ."(Saving address to file " type .")" cr save-address
} : save-address-verbose
// ( fname -- wc addr ) Load address from file
{ file>B 32 B|
dup Blen { 32 B>i@ } { drop Basechain } cond
swap 256 B>u@
} : load-address
// ( fname -- wc addr ) Load address from file and print message
{ dup ."(Loading address from file " type .")" cr load-address
} : load-address-verbose
// Parse string as address or load address from file (if string is prefixed by @)
// ( S default-bounce -- workchain addr bounce? )
{ over $len 0= abort"empty smart-contract address"
swap dup 1 $| swap "@" $=
{ nip load-address rot } { drop nip parse-smc-addr } cond
} : parse-load-address
// ( hex-str -- addr ) Parses ADNL address
{ dup $len 64 <> abort"ADNL address must consist of exactly 64 hexadecimal characters"
(hex-number) 1 <> abort"ADNL address must consist of 64 hexadecimal characters"
dup 256 ufits not abort"invalid ADNL address"
} : parse-adnl-address
// ( b wc addr -- b' ) Serializes address into Builder b
{ -rot 8 i, swap 256 u, } : addr,
// Gram utilities
1000000000 constant Gram
{ Gram swap */r } : Gram*/
{ Gram * } : Gram*
// ( S -- nanograms )
{ (number) ?dup 0= abort"not a valid Gram amount"
1- ' Gram*/ ' Gram* cond
} : $>GR
{ bl word $>GR 1 'nop } ::_ GR$
// ( nanograms -- S )
{ dup abs <# ' # 9 times char . hold #s rot sign #>
nip -trailing0 } : (.GR)
{ (.GR) ."GR$" type space } : .GR
// b x -- b' ( serializes a Gram amount )
{ -1 { 1+ 2dup 8 * ufits } until
rot over 4 u, -rot 8 * u, } : Gram,
// s -- x s' ( deserializes a Gram amount )
{ 4 u@+ swap 8 * u@+ } : Gram@+
// s -- x
{ 4 u@+ swap 8 * u@ } : Gram@
// currency collections
// b x --> b' ( serializes a VarUInteger32 )
{ -1 { 1+ 2dup 8 * ufits } until
rot over 5 u, -rot 8 * u, } : VarUInt32,
// s --> x ( deserializes a VarUInteger32 )
{ 5 u@+ swap 8 * u@ } : VarUInt32@
32 constant cc-key-bits
' VarUInt32, : val,
' VarUInt32@ : val@
// d k v -- d'
{ <b swap val, b> <s swap rot cc-key-bits idict! not abort"cannot add key-value to CurrencyCollection" } : +ccpair
dictnew constant cc0 // zero currency collection
// ( v k -- d ) Creates currency collection representing v units of currency k
{ cc0 swap rot +ccpair } : of-cc
{ dictnew { over null? not } { swap uncons -rot unpair +ccpair } while nip } : list>cc
{ dup null? { ."(null) " drop } { val@ . } cond } dup : .maybeVarUInt32 : .val
{ cc-key-bits { swap 32 1<< rmod . ."-> " .val ."; " true } dictforeach drop cr } : .cc
{ cc-key-bits { rot . ."-> " swap .val .val ."; " true } dictdiff drop cr } : show-cc-diff
{ cc-key-bits { val@ swap val@ + val, true } dictmerge } : cc+
{ null swap cc-key-bits { val@ pair swap cons true } dictforeach drop } : cc>list-rev
{ cc>list-rev list-reverse } : cc>list
forget val, forget val@ forget .val
// Libraries
// ( -- D ) New empty library collection
' dictnew : Libs{
// ( D -- D ) Return library collection as dictionary
'nop : }Libs
// ( D c x -- D' ) Add a public/private library c to collection D
{ <b swap 1 u, over ref, b> <s swap hash rot 256 udict!+
0= abort"duplicate library in collection" } : lib+
// ( D c -- D' ) Add private library c to collection D
{ 0 lib+ } : private_lib
// ( D c -- D' ) Add public library c to collection D
{ 1 lib+ } : public_lib