├── test ├── test_helper.exs └── forth2evm_test.exs ├── .gitignore ├── README.md ├── forth2evm ├── lib ├── Elixir.Assembler.beam ├── Elixir.Forth2EVM.beam ├── calc2.fs ├── calc2.fs~ ├── calc.fs~ ├── calc.fs ├── old_calc.fs ├── forth2evm.ex~ ├── forth2evm.ex └── assembler.ex ├── mix.exs └── config └── config.exs /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /deps 3 | erl_crash.dump 4 | *.ez 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Forth2evm 2 | ========= 3 | 4 | ** TODO: Add description ** 5 | -------------------------------------------------------------------------------- /forth2evm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zack-bitcoin/ethereum-forth/master/forth2evm -------------------------------------------------------------------------------- /lib/Elixir.Assembler.beam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zack-bitcoin/ethereum-forth/master/lib/Elixir.Assembler.beam -------------------------------------------------------------------------------- /lib/Elixir.Forth2EVM.beam: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zack-bitcoin/ethereum-forth/master/lib/Elixir.Forth2EVM.beam -------------------------------------------------------------------------------- /test/forth2evm_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Forth2evmTest do 2 | use ExUnit.Case 3 | 4 | test "the truth" do 5 | assert 1 + 1 == 2 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/calc2.fs: -------------------------------------------------------------------------------- 1 | \2 3 square square square square plus stop 2 | 9 9 9 9 9 4 5 do 3 | 4 | : logHelp ( a b c -- d ) 0 0 log2 ; 5 | 6 | : do ( a b c d -- a ) 6 logHelp logHelp ; 7 | 8 | -------------------------------------------------------------------------------- /lib/calc2.fs~: -------------------------------------------------------------------------------- 1 | 2 3 square square square square plus stop jumpdest death 0 0 log2 2 | 3 | : square ( a -- a*a ) dup1 mul ; 4 | : plus ( a b -- a+b ) add ; 5 | : minus ( a b -- a-b ) sub ; 6 | : times ( a b -- a*b ) mul ; 7 | : fib ( n -- f ) 0 0 fibby ; 8 | : fibby ( n a b -- n-1 b a+b) dup swap2 add swap2 1 sub dup 0 LT death jumpi swap2 fibby ; 9 | 10 | -------------------------------------------------------------------------------- /lib/calc.fs~: -------------------------------------------------------------------------------- 1 | / 2 3 square square square square plus stop jumpdest death 0 0 log2 2 | 2 3 0 0 log2 3 | 4 | : square ( a -- a*a ) dup1 mul ; 5 | : plus ( a b -- a+b ) add ; 6 | : minus ( a b -- a-b ) sub ; 7 | : times ( a b -- a*b ) mul ; 8 | : fib ( n -- f ) 0 0 fibby ; 9 | : fibby ( n a b -- n-1 b a+b) dup1 swap2 add swap2 1 sub dup1 0 LT death jumpi swap2 fibby ; 10 | 11 | -------------------------------------------------------------------------------- /lib/calc.fs: -------------------------------------------------------------------------------- 1 | \ 2 3 square square square square plus stop jumpdest death 0 0 log2 2 | 4 fib 0 0 log1 stop jumpdest death 3 | 4 | : square ( a -- a*a ) dup1 mul ; 5 | : plus ( a b -- a+b ) add ; 6 | : minus ( a b -- a-b ) sub ; 7 | : times ( a b -- a*b ) mul ; 8 | : fib ( n -- f ) 0 1 fibby ; 9 | : fibby ( n a b -- n-1 b a+b) dup1 swap2 add swap2 1 swap1 sub dup1 1 GT death jumpi swap2 fibby ; 10 | 11 | -------------------------------------------------------------------------------- /lib/old_calc.fs: -------------------------------------------------------------------------------- 1 | \2 3 square square square square plus stop 2 | 5 fib jumpdest death 3 | 4 | : square ( a -- a*a ) dup1 mul ; 5 | : plus ( a b -- a+b ) add ; 6 | : minus ( a b -- a-b ) sub ; 7 | : times ( a b -- a*b ) mul ; 8 | : fib ( n -- f ) 0 1 fibby ; 9 | : fibby ( n a b -- n-1 b a+b) dup1 swap2 add swap2 1 swap1 sub dup1 0 GT death jumpi swap2 fibby ; 10 | : fib_iter ( a b -- b a+b ) dup1 swap2 add 11 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Forth2evm.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [app: :forth2evm, 6 | version: "0.0.1", 7 | elixir: "~> 1.0", 8 | escript: escript, 9 | deps: deps] 10 | end 11 | def escript do 12 | [main_module: Forth2EVM] 13 | end 14 | 15 | 16 | # Configuration for the OTP application 17 | # 18 | # Type `mix help compile.app` for more information 19 | def application do 20 | [applications: [:logger]] 21 | end 22 | 23 | # Dependencies can be Hex packages: 24 | # 25 | # {:mydep, "~> 0.3.0"} 26 | # 27 | # Or git/path repositories: 28 | # 29 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} 30 | # 31 | # Type `mix help deps` for more examples and options 32 | defp deps do 33 | [] 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for third- 9 | # party users, it should be done in your mix.exs file. 10 | 11 | # Sample configuration: 12 | # 13 | # config :logger, :console, 14 | # level: :info, 15 | # format: "$date $time [$level] $metadata$message\n", 16 | # metadata: [:user_id] 17 | 18 | # It is also possible to import configuration files, relative to this 19 | # directory. For example, you can emulate configuration per environment 20 | # by uncommenting the line below and defining dev.exs, test.exs and such. 21 | # Configuration from the imported file will override the ones defined 22 | # here (which is why it is important to import them last). 23 | # 24 | # import_config "#{Mix.env}.exs" 25 | -------------------------------------------------------------------------------- /lib/forth2evm.ex~: -------------------------------------------------------------------------------- 1 | defmodule Forth2EVM do 2 | #comments are \ to \n and ( to ) 3 | #functions are : to ; 4 | def skip_till(text, symbol) do 5 | cond do 6 | text == "" -> "" 7 | true -> 8 | <> = text 9 | l=<> 10 | cond do 11 | l==symbol -> text 12 | true -> skip_till(text, symbol) 13 | end 14 | end 15 | end 16 | def outside(text, left, right) do 17 | cond do 18 | text=="" -> "" 19 | true -> 20 | <> = text 21 | l=<> 22 | cond do 23 | l==left -> outside(skip_till(text, right), left, right) 24 | not(text=="") -> l <> outside(text, left, right) 25 | true -> l 26 | end 27 | end 28 | end 29 | def inside(text, left, right) do outside(skip_till(text, left), right, left) end 30 | def words_code(file) do 31 | {:ok, text} = File.read file 32 | text = text |> outside("\\", "\n") |> String.replace(";", "\\;") |> outside("(", ")") 33 | words = text |> inside(":", ";") |> String.split("\\") |> Enum.filter(&(&1!="")) 34 | code = text |> outside(":", ";") |> String.replace("\n", " ") 35 | {words, code} 36 | end 37 | def remove_spaces(word) do word |> String.split(" ") |> Enum.filter(&(&1!="")) |> Enum.reduce("", &(&2<>" "<>&1)) end 38 | def encode_word(word) do " jumpdest "<>jumps(remove_spaces(word))<>" 0 mload dup1 32 sub 0 mstore mload jump " end 39 | def is_string_int(x) do 40 | cond do 41 | x=="" -> true 42 | true -> 43 | <>=x 44 | cond do 45 | a<48 or a>57 -> false 46 | true -> is_string_int(b) 47 | end 48 | end 49 | end 50 | def jumps(c) do 51 | c=remove_spaces(c) |> String.split(" ") 52 | jumps_helper(c) 53 | end 54 | def opcodes do ["STOP", "ADD", "MUL", "SUB","DIV","SDIV","MOD","SMOD","ADDMOD","MULMOD","EXP","SIGNEXTEND","LT","GT","SLT","SGT","EQ","ISZERO","AND","OR","XOR","NOT","BYTE","SHA3","ADDRESS","BALANCE","ORIGIN","CALLER","CALLVALUE","CALLDATALOAD","CALLDATASIZE","CALLDATACOPY","CODESIZE","CODECOPY","GASPRICE","EXTCODESIZE","EXTCODECOPY","BLOCKHASH","COINBASE","TIMESTAMP","NUMBER","DIFFICULTY","GASLIMIT","POP","MLOAD","MSTORE","MSTORES","SLOAD","SSTORE","JUMP","JUMPI","PC","MSIZE","GAS","JUMPDEST","PUSH1","PUSH2","PUSH3","PUSH4","PUSH5","PUSH6","PUSH7","PUSH8","PUSH9","PUSH10","PUSH11","PUSH12","PUSH13","PUSH14","PUSH15","PUSH16","PUSH17","PUSH18","PUSH19","PUSH20","PUSH21","PUSH22","PUSH23","PUSH24","PUSH25","PUSH26","PUSH27","PUSH28","PUSH29","PUSH30","PUSH31","PUSH32","DUP1","DUP2","DUP3","DUP4","DUP5","DUP6","DUP7","DUP8","DUP9","DUP10","DUP11","DUP12","DUP13","DUP14","DUP15","DUP16","SWAP1","SWAP2","SWAP3","SWAP4","SWAP5","SWAP6","SWAP7","SWAP8","SWAP9","SWAP10","SWAP11","SWAP12","SWAP13","SWAP14","SWAP15","SWAP16","LOG0","LOG1","LOG2","LOG3","LOG4"] end 55 | def jumps_helper(c) do 56 | cond do 57 | c==[] -> "" 58 | is_string_int(hd(c)) -> hd(c)<>" "<>jumps_helper(tl(c)) 59 | String.upcase(hd(c)) in opcodes -> 60 | hd(c)<>" "<>jumps_helper(tl(c)) 61 | length(c) == 1 -> hd(c)<>" jump " #tail call recursion! 62 | true -> 63 | IO.puts "long way" 64 | r=" "<>Enum.reduce(Enum.map(0..10, fn(x)-> 65 | <<:crypto.rand_uniform(66, 90)>> end), "", &(&1<>&2))<>" " 66 | r<>" 0 mload 32 add dup1 0 mstore mstore "<> hd(c) <> " jump jumpdest "<>r<>jumps_helper(tl(c)) 67 | end 68 | end 69 | def compile(file) do 70 | {w, c} = words_code(file) 71 | w = Enum.reduce(Enum.map(w, &(encode_word(&1))), &(&1<>&2)) 72 | c = jumps(c) 73 | Assembler.compile("0 0 mstore "<>c<>" 0 0 log1 stop "<>w) 74 | end 75 | def test do 76 | IO.puts compile("calc.fs") 77 | end 78 | def test_words_code do 79 | IO.puts inspect words_code("test.fs") 80 | end 81 | def test_out_in do 82 | {:ok, text} = File.read "test.fs" 83 | IO.puts inspect text 84 | IO.puts inspect inside(text, "(", ")") 85 | IO.puts inspect outside(text, "(", ")") 86 | end 87 | def main(args) do 88 | args=hd(args) 89 | cond do 90 | args==[] -> IO.puts "not enough args. example: ./assembler file.asm" 91 | args=="-h" -> IO.puts "usage: ./assembler code.asm" 92 | args=="--help" -> IO.puts "usage: ./assembler code.asm" 93 | true -> 94 | {:ok, text} = File.read args 95 | IO.puts compile(text) 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /lib/forth2evm.ex: -------------------------------------------------------------------------------- 1 | defmodule Forth2EVM do 2 | #comments are \ to \n and ( to ) 3 | #functions are : to ; 4 | def skip_till(text, symbol) do 5 | cond do 6 | text == "" -> "" 7 | true -> 8 | <> = text 9 | l=<> 10 | cond do 11 | l==symbol -> text 12 | true -> skip_till(text, symbol) 13 | end 14 | end 15 | end 16 | def outside(text, left, right) do 17 | cond do 18 | text=="" -> "" 19 | true -> 20 | <> = text 21 | l=<> 22 | cond do 23 | l==left -> outside(skip_till(text, right), left, right) 24 | not(text=="") -> l <> outside(text, left, right) 25 | true -> l 26 | end 27 | end 28 | end 29 | def inside(text, left, right) do outside(skip_till(text, left), right, left) end 30 | def words_code(text) do 31 | words = text |> String.replace(";", "\\;") |> inside(":", ";") |> String.split("\\") |> Enum.filter(&(&1!="")) 32 | code = text 33 | code = outside(code, ":", ";") 34 | code = String.replace(code, "\n", " ") 35 | {words, code} 36 | end 37 | def encode_word(c, jumpdests) do 38 | b=prettify(c) 39 | " jumpdest "<>hd(b)<>" "<>jumps_helper(tl(b), jumpdests)<>" 0 mload dup1 32 swap1 sub 0 mstore mload jump " end 40 | def is_string_int(x) do 41 | cond do 42 | x=="" -> true 43 | true -> 44 | <>=x 45 | cond do 46 | a<48 or a>57 -> false 47 | true -> is_string_int(b) 48 | end 49 | end 50 | end 51 | def opcodes do Enum.map(Dict.keys(Assembler.opcodes), &(to_string(&1))) end 52 | #def opcodes do ["STOP", "ADD", "MUL", "SUB","DIV","SDIV","MOD","SMOD","ADDMOD","MULMOD","EXP","SIGNEXTEND","LT","GT","SLT","SGT","EQ","ISZERO","AND","OR","XOR","NOT","BYTE","SHA3","ADDRESS","BALANCE","ORIGIN","CALLER","CALLVALUE","CALLDATALOAD","CALLDATASIZE","CALLDATACOPY","CODESIZE","CODECOPY","GASPRICE","EXTCODESIZE","EXTCODECOPY","BLOCKHASH","COINBASE","TIMESTAMP","NUMBER","DIFFICULTY","GASLIMIT","POP","MLOAD","MSTORE","MSTORES","SLOAD","SSTORE","JUMP","JUMPI","PC","MSIZE","GAS","JUMPDEST","PUSH1","PUSH2","PUSH3","PUSH4","PUSH5","PUSH6","PUSH7","PUSH8","PUSH9","PUSH10","PUSH11","PUSH12","PUSH13","PUSH14","PUSH15","PUSH16","PUSH17","PUSH18","PUSH19","PUSH20","PUSH21","PUSH22","PUSH23","PUSH24","PUSH25","PUSH26","PUSH27","PUSH28","PUSH29","PUSH30","PUSH31","PUSH32","DUP1","DUP2","DUP3","DUP4","DUP5","DUP6","DUP7","DUP8","DUP9","DUP10","DUP11","DUP12","DUP13","DUP14","DUP15","DUP16","SWAP1","SWAP2","SWAP3","SWAP4","SWAP5","SWAP6","SWAP7","SWAP8","SWAP9","SWAP10","SWAP11","SWAP12","SWAP13","SWAP14","SWAP15","SWAP16","LOG0","LOG1","LOG2","LOG3","LOG4"] end 53 | def jumps_helper(c, jumps) do 54 | cond do 55 | c==[] -> "" 56 | hd(c)==" " or hd(c)=="" -> jumps_helper(tl(c), jumps) 57 | is_string_int(hd(c)) -> hd(c)<>" "<>jumps_helper(tl(c), jumps) 58 | String.upcase(hd(c))=="JUMPDEST" -> 59 | hd(c)<>" "<>hd(tl(c))<>" "<>jumps_helper(tl(tl(c)), [hd(tl(c))|jumps]) 60 | String.upcase(hd(c)) in jumps and (length(c)==1 or String.upcase(hd(tl(c))))=="ELSE" -> #tail call optimization! 61 | hd(c)<>" JUMPI "<>jumps_helper(tl(c), jumps) 62 | String.upcase(hd(c)) in ["ELSE"|opcodes] -> 63 | hd(c)<>" "<>jumps_helper(tl(c), jumps) 64 | length(c) == 1 -> hd(c)<>" jump " 65 | true -> 66 | r=" "<>Enum.reduce(Enum.map(0..10, fn(x)-> 67 | <<:crypto.rand_uniform(66, 90)>> end), "", &(&1<>&2))<>" " 68 | r<>" 0 mload 32 add dup1 0 mstore mstore "<> hd(c) <> " Jump jumpdest "<>r<>jumps_helper(tl(c), jumps) 69 | end 70 | end 71 | def jds(t, d \\ []) do 72 | #IO.puts inspect t 73 | cond do 74 | t==[] -> d 75 | String.upcase(hd(t)) == "JUMPDEST" -> 76 | jds(tl(tl(t)), [hd(tl(t))|d]) 77 | true -> jds(tl(t), d) 78 | end 79 | end 80 | def prettify(c) do c |> String.split(" ") |> Enum.filter(&(&1!=""))end 81 | def concat(l) do l |> Enum.reduce("", &(&2<>" "<>&1)) end 82 | def apply_macros(macros, text) do 83 | #IO.puts "text #{inspect text}" 84 | #IO.puts "macros #{inspect macros}" 85 | cond do 86 | macros==[] -> text 87 | true -> apply_macros(tl(macros), apply_macro(text, hd(macros))) 88 | end 89 | end 90 | def apply_macro(text, macro) do 91 | macro= macro |> String.split(" ") |> Enum.filter(&(&1!="")) 92 | key=hd(macro) 93 | String.replace(text, key, concat(tl(macro))) 94 | end 95 | def compile(text) do 96 | text=text<>" stop" 97 | text = text |> outside("\\", "\n") |> outside("(", ")") |> String.replace("\n", " ")#remove comments 98 | #jumpdests = jds(String.split(String.replace(text, "\n", " "), " ")) #need to use this everywhere!!!! 99 | #IO.puts "text #{inspect text}" 100 | macros=text |> String.replace(";", "\\;") 101 | #IO.puts "macros #{inspect macros}" 102 | macros = macros |> inside("#", ";") 103 | text = text |> outside("#", ";") 104 | #IO.puts "text without macro #{inspect text}" 105 | #IO.puts "macros #{inspect macros}" 106 | macros = macros |> String.split("\\") 107 | #IO.puts "macros #{inspect macros}" 108 | macros = macros |> Enum.filter(&(&1!="")) 109 | #IO.puts "macros #{inspect macros}" 110 | text=text |> outside("#", ";") 111 | text=apply_macros(macros, text) 112 | {w, c} = words_code(text) 113 | #IO.puts "w c #{inspect w} #{inspect c}" 114 | jumpdests = w |> Enum.map(&(hd(prettify(&1)))) |> Enum.map(&(String.upcase(&1))) 115 | #IO.puts "jumpdests #{inspect jumpdests}" 116 | #IO.puts "w c #{inspect w} #{inspect c}" 117 | w = Enum.reduce(Enum.map(w, &(encode_word(&1, jumpdests))), &(&1<>&2)) 118 | c = prettify(c) 119 | c = jumps_helper(c, jumpdests) 120 | t=" 0 0 mstore "<>c<>" 0 0 log1 stop "<>w 121 | t = t |> prettify |> Enum.filter(&(String.upcase(&1)!="ELSE")) |> concat 122 | IO.puts "before assembler: "<>t 123 | Assembler.compile(t) 124 | end 125 | def test do 126 | {:ok, text} = File.read("calc2.fs") 127 | IO.puts compile(text) 128 | end 129 | def test_words_code do 130 | IO.puts inspect words_code("test.fs") 131 | end 132 | def test_out_in do 133 | {:ok, text} = File.read "test.fs" 134 | IO.puts inspect text 135 | IO.puts inspect inside(text, "(", ")") 136 | IO.puts inspect outside(text, "(", ")") 137 | end 138 | def main(args) do 139 | args=hd(args) 140 | cond do 141 | args==[] -> IO.puts "not enough args. example: ./assembler file.asm" 142 | args=="-h" -> IO.puts "usage: ./assembler code.asm" 143 | args=="--help" -> IO.puts "usage: ./assembler code.asm" 144 | true -> 145 | {:ok, text} = File.read args 146 | IO.puts compile(text) 147 | end 148 | end 149 | end 150 | -------------------------------------------------------------------------------- /lib/assembler.ex: -------------------------------------------------------------------------------- 1 | defmodule Assembler do 2 | def opcodes do 3 | [STOP: 0x00, 4 | ADD: 0x01, 5 | "+": 0x01, 6 | MUL: 0x02, 7 | "*": 0x02, 8 | SUB: 0x03, 9 | "-": 0x03, 10 | DIV: 0x04, 11 | "/": 0x04, 12 | SDIV: 0x05, 13 | MOD: 0x06, 14 | "%": 0x06, 15 | SMOD: 0x07, 16 | ADDMOD: 0x08, 17 | MULMOD: 0x09, 18 | EXP: 0x0a, 19 | SIGNEXTEND: 0x0b, 20 | LT: 0x10, 21 | GT: 0x11, 22 | SLT: 0x12, 23 | SGT: 0x13, 24 | EQ: 0x14, 25 | ISZERO: 0x15, 26 | "IF": 0x15, 27 | AND: 0x16, 28 | OR: 0x17, 29 | XOR: 0x18, 30 | NOT: 0x19, 31 | BYTE: 0x1a, 32 | SHA3: 0x20, 33 | ADDRESS: 0x30, 34 | BALANCE: 0x31, 35 | ORIGIN: 0x32, 36 | CALLER: 0x33, 37 | CALLVALUE: 0x34, 38 | CALLDATALOAD: 0x35, 39 | CALLDATASIZE: 0x36, 40 | CALLDATACOPY: 0x37, 41 | CODESIZE: 0x38, 42 | CODECOPY: 0x39, 43 | GASPRICE: 0x3a, 44 | EXTCODESIZE: 0x3b, 45 | EXTCODECOPY: 0x3c, 46 | BLOCKHASH: 0x40, 47 | COINBASE: 0x41, 48 | TIMESTAMP: 0x42, 49 | NUMBER: 0x43, 50 | DIFFICULTY: 0x44, 51 | GASLIMIT: 0x45, 52 | POP: 0x50, 53 | MLOAD: 0x51, 54 | MSTORE: 0x52, 55 | MSTORES: 0x53, 56 | SLOAD: 0x54, 57 | SSTORE: 0x55, 58 | JUMP: 0x56, 59 | JUMPI: 0x57, 60 | PC: 0x58, 61 | MSIZE: 0x59, 62 | GAS: 0x5a, 63 | JUMPDEST: 0x5b, 64 | PUSH1: 0x60, 65 | PUSH2: 0x61, 66 | PUSH3: 0x62, 67 | PUSH4: 0x63, 68 | PUSH5: 0x64, 69 | PUSH6: 0x65, 70 | PUSH7: 0x66, 71 | PUSH8: 0x67, 72 | PUSH9: 0x68, 73 | PUSH10: 0x69, 74 | PUSH11: 0x6a, 75 | PUSH12: 0x6b, 76 | PUSH13: 0x6c, 77 | PUSH14: 0x6d, 78 | PUSH15: 0x6e, 79 | PUSH16: 0x6f, 80 | PUSH17: 0x70, 81 | PUSH18: 0x71, 82 | PUSH19: 0x72, 83 | PUSH20: 0x73, 84 | PUSH21: 0x74, 85 | PUSH22: 0x75, 86 | PUSH23: 0x76, 87 | PUSH24: 0x77, 88 | PUSH25: 0x78, 89 | PUSH26: 0x79, 90 | PUSH27: 0x7a, 91 | PUSH28: 0x7b, 92 | PUSH29: 0x7c, 93 | PUSH30: 0x7d, 94 | PUSH31: 0x7e, 95 | PUSH32: 0x7f, 96 | DUP: 0x80, 97 | DUP1: 0x80, 98 | DUP2: 0x81, 99 | DUP3: 0x82, 100 | DUP4: 0x83, 101 | DUP5: 0x84, 102 | DUP6: 0x85, 103 | DUP7: 0x86, 104 | DUP8: 0x87, 105 | DUP9: 0x88, 106 | DUP10: 0x89, 107 | DUP11: 0x8a, 108 | DUP12: 0x8b, 109 | DUP13: 0x8c, 110 | DUP14: 0x8d, 111 | DUP15: 0x8e, 112 | DUP16: 0x8f, 113 | SWAP1: 0x90, 114 | SWAP2: 0x91, 115 | SWAP3: 0x92, 116 | SWAP4: 0x93, 117 | SWAP5: 0x94, 118 | SWAP6: 0x95, 119 | SWAP7: 0x96, 120 | SWAP8: 0x97, 121 | SWAP9: 0x98, 122 | SWAP10: 0x99, 123 | SWAP11: 0x9a, 124 | SWAP12: 0x9b, 125 | SWAP13: 0x9c, 126 | SWAP14: 0x9d, 127 | SWAP15: 0x9e, 128 | SWAP16: 0x9f, 129 | LOG0: 0xa0, 130 | LOG1: 0xa1, 131 | LOG2: 0xa2, 132 | LOG3: 0xa3, 133 | LOG4: 0xa4] 134 | end 135 | def upcase(x) do 136 | cond do 137 | is_atom(x) -> 138 | x |> to_string |> String.upcase |> String.to_atom 139 | true -> x 140 | end 141 | end 142 | def case_preprocessor(l) do 143 | cond do 144 | l==[] -> [] 145 | true -> 146 | t=case_preprocessor(tl(l)) 147 | cond do 148 | is_atom(hd(l)) -> [upcase(hd(l))|t] 149 | true -> [hd(l)|t] 150 | end 151 | end 152 | end 153 | def log256(x) do 154 | cond do 155 | x>0 -> :math.log(x)/:math.log(256) 156 | true -> 0 157 | end 158 | end 159 | def intsize(i) do#this math allows us to store small numbers in small numbers of bytes, instead of needing 32 bytes for each number. 160 | i=String.to_integer(to_string(i)) 161 | trunc(log256(i))+1 162 | end 163 | def op2hex(op) do 164 | op=upcase(op) 165 | cond do 166 | op in Dict.keys(opcodes) -> << Dict.get(opcodes, op) >> 167 | is_atom(op) and is_string_int(to_string(op)) -> 168 | i=String.to_integer(to_string(op)) 169 | s=intsize(i) 170 | t=s*8 171 | <<95+s,i::size(t)>> 172 | is_atom(op) -> IO.puts "ERROR undefined word #{inspect op}" 173 | true -> <> 174 | end 175 | end 176 | def ops2hex(ops) do Enum.reduce(ops, "", fn(a, x) -> 177 | #IO.puts "x a #{inspect x} #{inspect a}" 178 | x<>op2hex(a) end)end 179 | def find(h, ps) do 180 | cond do 181 | ps==[] -> 1=2 182 | true -> 183 | {a, b}=hd(ps) 184 | cond do 185 | b==h -> a 186 | true -> find(h, tl(ps)) 187 | end 188 | end 189 | end 190 | def hex2op(h) do find(h, opcodes) end 191 | def hex2ops(h) do 192 | <> = h 193 | out=hex2op(first) 194 | cond do 195 | b=="" -> [out] 196 | true -> [out|hex2ops(b)] 197 | end 198 | end 199 | def jumpdests(l) do 200 | names=jumpdests_names(l) 201 | IO.puts "jumpdest names: #{inspect names}" 202 | names=Enum.map(names, &({&1, 1})) 203 | IO.puts "jumpdest names: #{inspect names}" 204 | names=jumpdests_counter(l, names, 0) 205 | IO.puts "jumpdest names: #{inspect names}" 206 | names=jumpdests_counter(l, names, 0) 207 | IO.puts "jumpdest names: #{inspect names}" 208 | names=jumpdests_counter(l, names, 0) 209 | IO.puts "jumpdest names: #{inspect names}" 210 | names 211 | end 212 | def jumpdests_names(l) do 213 | cond do 214 | l==[] -> [] 215 | upcase(hd(l))==:JUMPDEST -> 216 | [hd(tl(l))|jumpdests_names(tl(tl(l)))] 217 | true -> 218 | jumpdests_names(tl(l)) 219 | end 220 | end 221 | def jumpdests_counter(l, names, counter) do 222 | cond do 223 | l==[] -> names 224 | hd(l)==:JUMPDEST -> 225 | names=Dict.put(names, hd(tl(l)), counter) 226 | jumpdests_counter(tl(tl(l)), names, counter+1) 227 | is_string_int(to_string(hd(l))) -> 228 | i=String.to_integer(to_string(hd(l))) 229 | digits=intsize(i) 230 | jumpdests_counter(tl(l), names, counter+1+digits) 231 | hd(l) in Dict.keys(opcodes) -> 232 | jumpdests_counter(tl(l), names, counter+1) 233 | hd(l) in Dict.keys(names) -> 234 | k=hd(l) 235 | b=Dict.get(names, k) 236 | jumpdests_counter(tl(l), names, counter+intsize(b)+1) 237 | true -> 238 | jumpdests_counter(tl(l), names, counter+2) 239 | end 240 | end 241 | def filter_jumpdest(code) do 242 | cond do 243 | code==[] -> [] 244 | upcase(hd(code))==:JUMPDEST -> [hd(code)|filter_jumpdest(tl(tl(code)))] 245 | true -> [hd(code)|filter_jumpdest(tl(code))] 246 | end 247 | end 248 | def replace_junk(code, name_locs) do 249 | cond do 250 | code == [] -> [] 251 | hd(code) in Dict.keys(name_locs) -> 252 | [String.to_atom(to_string(name_locs[hd(code)]))]++replace_junk(tl(code),name_locs) 253 | true -> [hd(code)|replace_junk(tl(code), name_locs)] 254 | end 255 | end 256 | def pointer_preprocessor(l) do 257 | j=jumpdests(l) #[name: location, name: location] 258 | names=Enum.map(j, &(elem(&1, 0))) 259 | l=filter_jumpdest(l) 260 | #IO.puts "before replacejunk #{inspect l}" 261 | #IO.puts "before replacejunk #{inspect j}" 262 | replace_junk(l, j) 263 | end 264 | def p_nibble(0) do "0" end 265 | def p_nibble(1) do "1" end 266 | def p_nibble(2) do "2" end 267 | def p_nibble(3) do "3" end 268 | def p_nibble(4) do "4" end 269 | def p_nibble(5) do "5" end 270 | def p_nibble(6) do "6" end 271 | def p_nibble(7) do "7" end 272 | def p_nibble(8) do "8" end 273 | def p_nibble(9) do "9" end 274 | def p_nibble(10) do "a" end 275 | def p_nibble(11) do "b" end 276 | def p_nibble(12) do "c" end 277 | def p_nibble(13) do "d" end 278 | def p_nibble(14) do "e" end 279 | def p_nibble(15) do "f" end 280 | def p_byte(b) do#converts an int in [0, 255] to a 2-letter hex string like "b3" 281 | a=div(b, 16) 282 | b=rem(b, 16) 283 | p_nibble(a)<>p_nibble(b) 284 | end 285 | def hex2pretty(h) do 286 | <> = h 287 | a=p_byte(a) 288 | cond do 289 | h=="" -> a 290 | true -> a<>hex2pretty(h) 291 | end 292 | end 293 | def compile(t) do text2copy_paste(t) end 294 | def text2copy_paste(text) do 295 | text=text |> read_code |> case_preprocessor |> pointer_preprocessor |> ops2hex |> hex2pretty end 296 | def sints do ["0", "1", "2", "3" ,"4", "5", "6", "7", "8", "9"]end 297 | def is_string_int(s) do 298 | <> = s 299 | b=<> in sints 300 | cond do 301 | not(b) -> false 302 | s=="" -> true 303 | true -> is_string_int(s) 304 | end 305 | end 306 | def read_code_helper(t) do #should be a one-liner reduce statement. 307 | cond do 308 | t==[] -> [] 309 | true -> [String.to_atom(hd(t))|read_code_helper(tl(t))] 310 | end 311 | end 312 | def read_code(t) do 313 | t |> String.replace("\n", " ") |> String.replace("\t", " ") |> String.split(" ") |> Enum.filter(&(&1!="")) |> read_code_helper 314 | end 315 | def test_all do 316 | {:ok, text} = File.read "test.asm" 317 | IO.puts inspect text2copy_paste(text) 318 | end 319 | def test_compilation_steps do 320 | #code=[:jumpdest, :apple, :"1", :"2", :add, :jumpdest, :pear, :apple, :jump, :pear, :jump] 321 | code=[:jumpdest, :apple, :"1", :"2", :add, :apple, :jump] 322 | IO.puts "code #{inspect code}" 323 | code=case_preprocessor(code) 324 | IO.puts "code #{inspect code}" 325 | code=pointer_preprocessor(code) 326 | IO.puts "code #{inspect code}" 327 | code=ops2hex(code) 328 | IO.puts "code #{inspect code}" 329 | code = hex2pretty(code) 330 | IO.puts "code #{inspect code}" 331 | end 332 | def test_ops2hex do 333 | IO.puts inspect ops2hex([:swap14, :log1, 1]) 334 | end 335 | def test_hex2ops do 336 | IO.puts inspect hex2ops(<<0,1,2,3>>) 337 | end 338 | def test_hex_ops_hex do 339 | IO.puts inspect ops2hex(hex2ops(<<0,1,2,3>>)) 340 | end 341 | def main(args) do 342 | args=hd(args) 343 | cond do 344 | args==[] -> IO.puts "not enough args. example: ./assembler file.asm" 345 | args=="-h" -> IO.puts "usage: ./assembler code.asm" 346 | args=="--help" -> IO.puts "usage: ./assembler code.asm" 347 | true -> 348 | {:ok, text} = File.read args 349 | IO.puts compile(text) 350 | end 351 | end 352 | end 353 | --------------------------------------------------------------------------------