Skip to content

Commit

Permalink
Code cleanup & further implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
z80dev committed Oct 19, 2024
1 parent 4a809ff commit ca735fe
Show file tree
Hide file tree
Showing 11 changed files with 710 additions and 56 deletions.
18 changes: 18 additions & 0 deletions main.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#lang racket
(require racket/cmdline "src/puff.rkt")

(define filename "")

(define compilation-output 'bytecode)

(command-line
#:program "puff"
#:once-any
[("-b" "--bytecode") "Output bytecode" (set! compilation-output 'bytecode)]
[("-r" "--runtime-bytecode") "Output runtime bytecode" (set! compilation-output 'runtime)]
#:args (f)
(set! filename f))

(match compilation-output
['bytecode (displayln (compile-filename filename))]
['runtime (displayln (compile-filename-runtime filename))])
2 changes: 0 additions & 2 deletions parser.rkt

This file was deleted.

115 changes: 115 additions & 0 deletions src/analysis.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#lang racket

(require "lexer.rkt"
"huffparser.rkt"
"utils.rkt"
threading)

;; some structs, for convenience getter/setter methods
;; program-data will contain all the data required to compile a contract
(struct program-data (labels
macros
functions
fndecls
eventdefs
errordefs
constants
errors
includes
ctx) #:mutable)

;; no-arg constructor
(define (make-program-data)
(program-data (make-hash)
(make-hash)
(make-hash)
(make-hash)
(make-hash)
(make-hash)
(make-hash)
(make-hash)
(list)
(make-hash)))

;; not really needed, but a struct for a specific macro's data
;; TODO: decide if we should get rid of this and just use the list
(struct macro-data (args takes returns body))

;; constructor
(define (make-macro-data defmacro)
(apply macro-data defmacro))


;; analyze all top-level nodes, outputting into the same data object
(define (analyze-program program data)
(for-each (lambda (n) (analyze-node n data)) (rest program)))

;; save each macro body in the data object
(define (analyze-defmacro defmacro data)
(match defmacro
[(list 'defmacro identifier args takes returns body) (hash-set! (program-data-macros data) identifier (list args takes returns body))]
[_ (error "Invalid defmacro")]))

;; save each function body in the data object
(define (analyze-defn defn data)
(match defn
[(list 'defn identifier args takes returns body) (hash-set! (program-data-functions data) identifier (list args takes returns body))]
[_ (error "Invalid defn")]))

;; save each constant value in the data object
(define (analyze-defconst defconst data)
(match defconst
[(list 'defconst identifier value) (hash-set! (program-data-constants data) identifier value)]
[_ (error "Invalid defconst")]))

#| IMPORT HANDLING |#
;; macro to save the current context and restore it after the analysis
;; this is used for includes, which need to know the current file's directory
;; so we temporarily set the context to one with the include's filename
(define-syntax with-temp-context
(syntax-rules ()
[(_ data ctx body ...)
(let ([old-ctx (program-data-ctx data)])
(set-program-data-ctx! data ctx)
(begin
body ...
(set-program-data-ctx! data old-ctx)))]))

(define (analyze-filename filename data)
(let ([parse-tree (~> filename
file->string
lex
parse
syntax->datum)])
(with-temp-context data (hash 'filename filename)
(analyze-node parse-tree data))))

(define (analyze-include inc data)
(let* ([current-file (hash-ref (program-data-ctx data) 'filename)]
[current-dir (path->string (path-only (path->complete-path current-file)))])
(parameterize ([current-directory current-dir])
(match inc
[(list 'include filename) (let* ([filename (string-append current-dir (format-filename filename))])
(set-program-data-includes! data (cons filename (program-data-includes data)))
(analyze-filename filename data))]
[_ (error "Invalid include")]))))
#| END IMPORT HANDLING |#


;; top-level node-handler function
(define (analyze-node node [data #f] [ctx #f])
(let ([data (or data (make-program-data))])
(when ctx (set-program-data-ctx! data ctx))
(match (first node)
['program (analyze-program node data)]
['defmacro (analyze-defmacro node data)]
['include (analyze-include node data)]
['defconst (analyze-defconst node data)]
['defn (analyze-defn node data)])
data))

(provide (struct-out program-data)
(struct-out macro-data)
make-program-data
make-macro-data
analyze-node)
177 changes: 177 additions & 0 deletions src/assembler.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#lang racket

#|
This module provides a simple assembler for Ethereum Virtual Machine (EVM) opcodes.
It provides a mapping from opcode names to their hexadecimal values, and functions
to convert between opcode names and their hexadecimal values.
The module also provides functions to convert between bytes and hexadecimal strings.
|#

(define opcode-map
(hash "STOP" #x00
"ADD" #x01
"MUL" #x02
"SUB" #x03
"DIV" #x04
"SDIV" #x05
"MOD" #x06
"SMOD" #x07
"ADDMOD" #x08
"MULMOD" #x09
"EXP" #x0a
"SIGNEXTEND" #x0b
"LT" #x10
"GT" #x11
"SLT" #x12
"SGT" #x13
"EQ" #x14
"ISZERO" #x15
"AND" #x16
"OR" #x17
"XOR" #x18
"NOT" #x19
"BYTE" #x1a
"SHL" #x1b
"SHR" #x1c
"SAR" #x1d
"KECCAK256" #x20
"ADDRESS" #x30
"BALANCE" #x31
"ORIGIN" #x32
"CALLER" #x33
"CALLVALUE" #x34
"CALLDATALOAD" #x35
"CALLDATASIZE" #x36
"CALLDATACOPY" #x37
"CODESIZE" #x38
"CODECOPY" #x39
"GASPRICE" #x3a
"EXTCODESIZE" #x3b
"EXTCODECOPY" #x3c
"RETURNDATASIZE" #x3d
"RETURNDATACOPY" #x3e
"EXTCODEHASH" #x3f
"BLOCKHASH" #x40
"COINBASE" #x41
"TIMESTAMP" #x42
"NUMBER" #x43
"PREVRANDAO" #x44
"GASLIMIT" #x45
"CHAINID" #x46
"SELFBALANCE" #x47
"BASEFEE" #x48
"BLOBHASH" #x49
"BLOBBASEFEE" #x4a
"POP" #x50
"MLOAD" #x51
"MSTORE" #x52
"MSTORE8" #x53
"SLOAD" #x54
"SSTORE" #x55
"JUMP" #x56
"JUMPI" #x57
"PC" #x58
"MSIZE" #x59
"GAS" #x5a
"JUMPDEST" #x5b
"TLOAD" #x5C
"TSTORE" #x5D
"MCOPY" #x5E
"PUSH0" #x5F
"PUSH1" #x60
"PUSH2" #x61
"PUSH3" #x62
"PUSH4" #x63
"PUSH5" #x64
"PUSH6" #x65
"PUSH7" #x66
"PUSH8" #x67
"PUSH9" #x68
"PUSH10" #x69
"PUSH11" #x6A
"PUSH12" #x6B
"PUSH13" #x6C
"PUSH14" #x6D
"PUSH15" #x6E
"PUSH16" #x6F
"PUSH17" #x70
"PUSH18" #x71
"PUSH19" #x72
"PUSH20" #x73
"PUSH21" #x74
"PUSH22" #x75
"PUSH23" #x76
"PUSH24" #x77
"PUSH25" #x78
"PUSH26" #x79
"PUSH27" #x7A
"PUSH28" #x7B
"PUSH29" #x7C
"PUSH30" #x7D
"PUSH31" #x7E
"PUSH32" #x7F
"DUP1" #x80
"DUP2" #x81
"DUP3" #x82
"DUP4" #x83
"DUP5" #x84
"DUP6" #x85
"DUP7" #x86
"DUP8" #x87
"DUP9" #x88
"DUP10" #x89
"DUP11" #x8A
"DUP12" #x8B
"DUP13" #x8C
"DUP14" #x8D
"DUP15" #x8E
"DUP16" #x8F
"SWAP1" #x90
"SWAP2" #x91
"SWAP3" #x92
"SWAP4" #x93
"SWAP5" #x94
"SWAP6" #x95
"SWAP7" #x96
"SWAP8" #x97
"SWAP9" #x98
"SWAP10" #x99
"SWAP11" #x9A
"SWAP12" #x9B
"SWAP13" #x9C
"SWAP14" #x9D
"SWAP15" #x9E
"SWAP16" #x9F
"LOG0" #xA0
"LOG1" #xA1
"LOG2" #xA2
"LOG3" #xA3
"LOG4" #xA4
"CREATE" #xF0
"CALL" #xF1
"CALLCODE" #xF2
"RETURN" #xF3
"DELEGATECALL" #xF4
"CREATE2" #xF5
"STATICCALL" #xFA
"REVERT" #xFD
"INVALID" #xFE
"SELFDESTRUCT" #xFF))

(define opcodes (hash-keys opcode-map))

(define (byte->opcode)
(let ([byte (read-byte)])
(hash-ref opcode-map byte)))

(define (assemble-opcode opcode)
(cond
[(hash-has-key? opcode-map opcode) (hash-ref opcode-map opcode)]
[(string-prefix? opcode "0x") (string->number (substring opcode 2) 16)]
[else (error "Unknown opcode" opcode)]))

(define (assemble-opcodes opcodes)
(map assemble-opcode opcodes))

(provide opcode-map opcodes assemble-opcode assemble-opcodes)
45 changes: 45 additions & 0 deletions src/codegen.rkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#lang racket
(require "huff-ops.rkt"
"utils.rkt"
threading)

;; in this file: functions to generate actual opcodes
;; this means:
;; - huff instructions like mstore become "MSTORE"
;; - hex values like "0x20" become "PUSH1 0x20"
;; - constructor generators

(define (handle-val val)
(cond
[(instruction? val) (list (instruction->opcode val))]
[else (begin
(displayln (format "Unknown value: ~a" val))
(list (string-upcase (symbol->string val))))]))

(define (handle-hex val)
(if (equal? val "0x00")
(list "PUSH0")
(let* (
[num-bytes (ceiling (/ (- (string-length val) 2) 2))]
[push-instr (string-append "PUSH" (number->string num-bytes))])
(list push-instr val))))

(define (handle-expr expr)
(match (first expr)
['hex (handle-hex (second expr))]
['const-ref (list expr)]
['body (apply append (map handle-tree (rest expr)))]))

(define (handle-tree tree)
(if (list? tree)
(handle-expr tree)
(handle-val tree)))

(define (generate-copy-constructor sz)
(let ([sz-hex-str (string-append "0x" (number->string sz 16))])
(append (handle-hex sz-hex-str) '("DUP1" "PUSH1" "0x09" "RETURNDATASIZE" "CODECOPY" "RETURNDATASIZE" "RETURN"))))

(provide handle-tree
handle-hex
handle-val
generate-copy-constructor)
Loading

0 comments on commit ca735fe

Please sign in to comment.