mist

package module
v0.0.0-...-93b4cde Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 22, 2024 License: GPL-3.0 Imports: 14 Imported by: 0

README

Mist

The much-needed Lisp language for the Ethereum EVM.

Running Mist
$ make
$ ./mist < source.mist
Quickstart

Want to waste some resources? Say no more:

(defun fib (i)
  (if (< i 2)
      i
    (+ (fib (- i 2))
       (fib (- i 1)))))

(return (fib 0xBAD))

Public contracts most likely would include a dispatcher:

(defvar *balances*    (mapping address uint256))
(defvar *allowances*  (mapping address (mapping address uint256)))
(defvar *totalSupply* uint256)

(defun totalSupply () *totalSupply*)
(defun balanceOf (address) (gethash *balances* address))
(defun allowance (owner spender) (gethash *allowances* owner spender))

(defun transfer (to value) ...)
(defun transferFrom (from to value) ...)

(defun approve (spender value)
  (puthash *allowances* value (caller) spender)
  (emit3 "Approval(address,address,uint256)" owner spender value)
  t)

(dispatch
 ("totalSupply()"                          totalSupply)
 ("balanceOf(address)"                       balanceOf)
 ("allowance(address,address)"               allowance)
 ("transfer(address,uint256)"                 transfer)
 ("transferFrom(address,address,uint256)" transferFrom)
 ("approve(address,uint256)"                   approve))

For an example implementation of an ERC-20 token, please see charm.mist.

Standard library

nil (false) values:
  • the number 0
  • the symbol nil
  • any empty list, quoted or not
t (true) values:
  • any number other than 0
  • the symbol t
  • any non-empty list, quoted or not
Functions mapped to native instructions
  • (stop)
  • (-) e.g. (- 20 5)
  • (/) e.g. (/ 10 2)
  • (%) e.g. (% 10 3)
  • (+%) e.g. (+% a b c), shorthand for (% (+ a b) c)
  • (*%) e.g. (*% a b c), shorthand for (% (* a b) c)
  • (**) e.g. (** x y), x to the power of y
  • (expt), an alias for (**)
  • (<) e.g. (< a b) results in t if a<b, nil otherwise
  • (>)
  • (=) e.g. (= a b) results in t if a==b, nil otherwise
  • (not) e.g. (not t) results in nil and (not nil) results in t
  • (zerop), an alias for (not)
  • (~), e.g. (~ a) returns the bitwise complement of a
  • (lognot), an alias for (~)
  • (byte), e.g. (byte index word), see opcode BYTE
  • (<<), e.g. (<< a b) returns integer a with its bits shifted by b bit positions, (<< 1 4) results in 16
  • (>>)
  • (current-address), see opcode ADDRESS
  • (balance), see opcode BALANCE
  • (origin), see opcode ORIGIN
  • (caller), a.k.a. msg.sender
  • (call-value), value sent with the transaction
  • (calldata-load), low-level access to input transaction data
  • (calldata-size), low-level access to the size of the input data
  • (code-size), low-level access to the size of the currently running code
  • (gas-price), a.k.a. effective gas price
  • (coinbase), current block's beneficiary address
  • (timestamp), current block's timestamp
  • (block-number), current block's number
  • (prev-randao), latest RANDAO mix of the post beacon state of the previous block
  • (gas-limit), current block's gas limit
  • (chain-id)
  • (self-balance), balance of currently executing account
  • (base-fee), current block's base fee
  • (available-gas), amount of available gas (after paying for this instruction)
Variadic:
  • (+), e.g. (+ 1 2 3 4 5)
  • (*)
  • (&) and its alias (logand)
  • (|) and its alias (logior)
  • (&) and its alias (logxor)
Builtins:
  • (case), standard Lisp (case), see examples/case*.mist for examples
  • (defconst), give a name to a constant expression
  • (defun), e.g. (defun NAME ARGLIST BODY...), define NAME as function
  • (defvar), e.g. (defvar totalSupply uint256), create a storage variable
  • (emit3), e.g. (emit3 "Transfer(address,address,uint256)" from to value), emit a Log with 3 topics
  • (ether), e.g. (ether "1") results in 1e18
  • (gethash TABLE KEYS...), access values in a mapping, e.g. (gethash balances owner) or (gethash allowances owner spender)
  • (if COND A B) results in A if COND holds and B otherwise
  • (progn BODY...) executes all BODY expressions in a sequence and yields the result of the last one
  • (puthash TABLE VALUE KEYS...), analogous to (gethash), e.g. (puthash balances value owner) or (puthash allowances value owner spender)
  • (return VALUE-OR-STRING)
  • (revert VALUE-OR-STRING)
  • (selector STRING)
  • (setq SYMBOL VALUE) assigns VALUE to the storage variable named SYMBOL
Macros:
  • (<=)
  • (>=)
  • (apply FUNCTION ARGUMENTS...)
  • (dispatch), see examples/token.mist
  • (let VARLIST BODY...)
  • (unless COND BODY...) if COND yields nil, do BODY, else return nil
  • (when COND BODY...) if COND yields t, do BODY, else return nil
Notes:

These are the only functions do not result in an expression:

  • (stop)
  • (return x)
  • (revert x)
Limitations:
  • Code length can't exceed 2^16 bytes (64 kilobytes).
TODO:
  • Implement real macros.
  • Proper error reporting, no panic()s.
  • Segment should be an interface instead of a stateful mess.
  • Solidity offers a rich assortment of opcode optimizations; perhaps reuse?
  • Clean up the public/private mess.
  • Proper documentation, more examples, and more tests.
  • Deal with linters.

Documentation

Index

Constants

View Source
const (
	NodeList   = iota // 0
	NodeNumber        // 1
	NodeString        // 2
	NodeSymbol        // 3

)
View Source
const (
	TokenLeftParen  = iota // (
	TokenRightParen        // )
	TokenQuote             // '
	TokenNumber
	TokenString
	TokenSymbol
)
View Source
const (
	OffoptArithmetic = 1 << iota
	OffoptIf         = 1 << iota
)

Flags to turn off optimizations.

View Source
const (
	// HashLength is the expected length of the hash
	HashLength = 32
)

Variables

This section is empty.

Functions

func Compile

func Compile(program, source string, init bool, offopt uint32) (string, error)

func Decompile

func Decompile(program string) string

func EncodeRLP

func EncodeRLP(x any) string

func EncodeString

func EncodeString(s string) string

func EncodeWithSignature

func EncodeWithSignature(signature string, args ...any) string

func MakeConstructor

func MakeConstructor(deployedBytecode string) string

func NewCompilationError

func NewCompilationError(origin Origin, message string) error

func NewLexicalError

func NewLexicalError(filename string, line, column int, message, token string) error

func NumArguments

func NumArguments(signature string) int

func SegmentsGetPosition

func SegmentsGetPosition(xs []Segment, id int32) int

func SegmentsToString

func SegmentsToString(xs []Segment) string

func VisitSequence

func VisitSequence(v Visitor, s *Scope, esp int, nodes []Node, dir int) int

Types

type BytecodeVisitor

type BytecodeVisitor struct {
	// contains filtered or unexported fields
}

func NewBytecodeVisitor

func NewBytecodeVisitor(init bool) *BytecodeVisitor

func (*BytecodeVisitor) GetOptimizedSegments

func (v *BytecodeVisitor) GetOptimizedSegments() []Segment

func (*BytecodeVisitor) VisitFunction

func (v *BytecodeVisitor) VisitFunction(s *Scope, esp int, call Node)

func (*BytecodeVisitor) VisitNil

func (v *BytecodeVisitor) VisitNil()

func (*BytecodeVisitor) VisitNumber

func (v *BytecodeVisitor) VisitNumber(x *uint256.Int)

func (*BytecodeVisitor) VisitString

func (v *BytecodeVisitor) VisitString(n Node)

func (*BytecodeVisitor) VisitSymbol

func (v *BytecodeVisitor) VisitSymbol(s *Scope, esp int, symbol Node)

func (*BytecodeVisitor) VisitT

func (v *BytecodeVisitor) VisitT()

type CompilationError

type CompilationError struct {
	Origin  Origin
	Message string
}

func (CompilationError) Error

func (e CompilationError) Error() string

type Hash

type Hash [HashLength]byte

func Keccak256Hash

func Keccak256Hash(data ...[]byte) Hash

type KeccakState

type KeccakState interface {
	hash.Hash
	Read(p []byte) (int, error)
}

type LexicalError

type LexicalError struct {
	Origin  Origin
	Message string
	Token   string
}

func (LexicalError) Error

func (e LexicalError) Error() string

type LispFunction

type LispFunction struct {
	Origin Origin
	Name   string
	Args   []Node
	Body   Node // Wrapped in (progn).
}

func NewLispFunction

func NewLispFunction(n Node) (LispFunction, error)

type Node

type Node struct {
	Type int

	ValueString string
	ValueNumber *uint256.Int

	Children []Node

	Origin Origin
}

func NewNodeApplication

func NewNodeApplication(functionName string, origin Origin) Node

func NewNodeList

func NewNodeList(origin Origin) Node

func NewNodeNil

func NewNodeNil(origin Origin) Node

func NewNodeProgn

func NewNodeProgn() Node

func NewNodeQuote

func NewNodeQuote(child Node, origin Origin) Node

func NewNodeString

func NewNodeString(value string, origin Origin) Node

func NewNodeSymbol

func NewNodeSymbol(symbol string, origin Origin) Node

func NewNodeU256

func NewNodeU256(x *uint256.Int, origin Origin) Node

func NewNodeU64

func NewNodeU64(x uint64, origin Origin) Node

func OptimizeAST

func OptimizeAST(node Node, offs uint32) Node

func Parse

func Parse(tokens *TokenIterator) Node

func (*Node) Accept

func (n *Node) Accept(v Visitor, s *Scope, esp int)

func (*Node) AddChild

func (n *Node) AddChild(child Node)

TODO: Maybe accepting (Node) is better? And then:

parent = parent.AddChild(child)

func (*Node) AddChildren

func (n *Node) AddChildren(children []Node)

func (*Node) FunctionName

func (n *Node) FunctionName() string

func (*Node) IsAtom

func (n *Node) IsAtom() bool

func (*Node) IsConstant

func (n *Node) IsConstant() bool

func (*Node) IsEmptyList

func (n *Node) IsEmptyList() bool

func (*Node) IsFunctionCall

func (n *Node) IsFunctionCall(name string) bool

func (*Node) IsList

func (n *Node) IsList() bool

func (*Node) IsNil

func (n *Node) IsNil() bool

func (*Node) IsQuote

func (n *Node) IsQuote() bool

func (*Node) IsString

func (n *Node) IsString() bool

func (*Node) IsSymbol

func (n *Node) IsSymbol() bool

func (*Node) IsT

func (n *Node) IsT() bool

func (*Node) IsThisSymbol

func (n *Node) IsThisSymbol(s string) bool

func (*Node) NumChildren

func (n *Node) NumChildren() int

func (*Node) String

func (n *Node) String() string

type Origin

type Origin struct {
	Filename string
	Line     int
	Column   int
}

func NewOrigin

func NewOrigin(filename string, line, column int) Origin

func NewOriginEmpty

func NewOriginEmpty() Origin

func (Origin) String

func (o Origin) String() string

type Scope

type Scope struct {
	Constants     map[string]Node
	Functions     map[string]LispFunction
	CallAddresses map[string]int32

	StackVariables   map[string]StackVariable
	StorageVariables map[string]int32

	Parent *Scope
}

func NewGlobalScope

func NewGlobalScope() *Scope

func NewScope

func NewScope(parent *Scope) *Scope

func (*Scope) Defconst

func (s *Scope) Defconst(identifier string, value Node)

func (*Scope) Defun

func (s *Scope) Defun(fn LispFunction)

func (*Scope) GetCallAddress

func (s *Scope) GetCallAddress(identifier string) (int32, bool)

func (*Scope) GetConstant

func (s *Scope) GetConstant(identifier string) (Node, bool)

func (*Scope) GetFunction

func (s *Scope) GetFunction(identifier string) (LispFunction, bool)

func (*Scope) GetStackVariable

func (s *Scope) GetStackVariable(identifier string) (StackVariable, bool)

func (*Scope) GetStorageVariable

func (s *Scope) GetStorageVariable(identifier string) (int32, bool)

func (*Scope) IsGlobal

func (s *Scope) IsGlobal() bool

func (*Scope) NewChildScope

func (s *Scope) NewChildScope() *Scope

func (*Scope) SetCallAddress

func (s *Scope) SetCallAddress(identifier string, segmentID int32)

func (*Scope) SetStackVariable

func (s *Scope) SetStackVariable(identifier string, variable StackVariable)

func (*Scope) SetStorageVariable

func (s *Scope) SetStorageVariable(name string, position int32)

type Segment

type Segment struct {
	// contains filtered or unexported fields
}

func OptimizeBytecode

func OptimizeBytecode(segments []Segment) []Segment

func SegmentsPopulatePointers

func SegmentsPopulatePointers(xs []Segment) []Segment

func (Segment) String

func (s Segment) String() string

type StackVariable

type StackVariable struct {
	Origin     Origin
	Identifier string
	Position   int // Absolute position in stack.
}

type StoredFunction

type StoredFunction struct {
	// contains filtered or unexported fields
}

type Token

type Token struct {
	Type        int
	ValueString string       // Symbolic or string value.
	ValueNumber *uint256.Int // Numeric value.
	Origin      Origin
}

func (Token) Short

func (t Token) Short() string

func (Token) String

func (t Token) String() string

type TokenIterator

type TokenIterator struct {
	// contains filtered or unexported fields
}

func NewTokenIterator

func NewTokenIterator() TokenIterator

func Scan

func Scan(code string, filename string) (TokenIterator, error)

func (*TokenIterator) HasNext

func (i *TokenIterator) HasNext() bool

func (*TokenIterator) Next

func (i *TokenIterator) Next() Token

func (*TokenIterator) Peek

func (i *TokenIterator) Peek() Token

type Visitor

type Visitor interface {
	// Simple literals, do not need a scope.
	VisitNil()
	VisitT()
	VisitNumber(value *uint256.Int)
	VisitString(node Node)

	// Symbols currently serve only as const/variable identifiers.
	VisitSymbol(s *Scope, esp int, symbol Node)

	VisitFunction(s *Scope, esp int, call Node)
}

Each visit function returns the stack delta, i.e. how many stack elements were pushed or taken.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL