├── .gitignore ├── README.md ├── prol.rb └── screenshot ├── demo.png └── demo.svg /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Yet another lisp interpreter 2 | 3 | **PRO**cessor of **L**ist for Mathematical Calculation 4 | 5 | It's a simple minimal lisp interpreter written in ruby. 6 | 7 | ![](./screenshot/demo.png) 8 | 9 | ### Feature 10 | 11 | - Basic functions as a minimal lisp 12 | - **No** support for `String` 13 | 14 | ### Usage 15 | 16 | ```bash 17 | $ ruby ./prol.rb 18 | ``` 19 | 20 | ### Acknowledgement 21 | 22 | [Tesla I. Zhang](https://github.com/ice1000), for pointing out errors and patient guidance -------------------------------------------------------------------------------- /prol.rb: -------------------------------------------------------------------------------- 1 | # It's a minimal lisp interpreter written in Ruby-lang 2 | # Author: @vonhyou 3 | # Start at: Apr. 10, 2021 4 | 5 | ##### Parser 6 | 7 | module Prolisp 8 | 9 | def self.parse(program) 10 | read_tokens(tokenize(program)) 11 | end 12 | 13 | def self.tokenize(program) 14 | # Convert scripts to token lists 15 | program.gsub('(', ' ( ').gsub(')', ' ) ').split 16 | end 17 | 18 | def self.make_list(tokens) 19 | lst = [] 20 | lst << read_tokens(tokens) while tokens[0] != ')' 21 | tokens.shift 22 | lst 23 | end 24 | 25 | def self.read_tokens(tokens) 26 | # read expressions from token 27 | raise SyntaxError, 'Unexpected EOF' if tokens.empty? 28 | 29 | case token = tokens.shift 30 | when '(' 31 | make_list tokens 32 | when ')' 33 | raise SyntaxError, "Unexpected ')'" 34 | else 35 | atom token 36 | end 37 | end 38 | 39 | def self.atom(token) 40 | # Analyse numbers and symbols 41 | case token 42 | when /\d/ 43 | (token.to_f % 1).positive? ? token.to_f : token.to_i 44 | else 45 | token.to_sym 46 | end 47 | end 48 | 49 | ##### Environments 50 | 51 | def self.make_global 52 | @global_env ||= begin 53 | ops = %i[== != < <= > >= + - * / % & | ^ ~] 54 | ops.inject({}) do |sp, op| 55 | sp.merge op => ->(*args) { args.inject(&op) } 56 | end 57 | end 58 | 59 | @global_env.merge! quote: ->(*args) { args.to_a } 60 | @global_env.merge! cons: ->(*args) { args.to_a } 61 | @global_env.merge! car: ->(arr) { arr[0] } 62 | @global_env.merge! cdr: ->(arr) { arr[1..-1] } 63 | @global_env.merge! print: ->(arg) { p arg } 64 | @global_env.merge! min: ->(arr) { arr.min } 65 | @global_env.merge! max: ->(arr) { arr.max } 66 | @global_env 67 | end 68 | 69 | ##### Lisp Eval 70 | def self.lisp_eval(elem, env = make_global) 71 | if elem.instance_of? Symbol 72 | env[elem] 73 | elsif elem.instance_of?(Integer) || elem.instance_of?(Float) 74 | elem 75 | elsif elem[0] == :def 76 | _, sym, exp = elem 77 | env[sym] = lisp_eval(exp, env) 78 | elsif elem[0] == :if 79 | _, cod, if_true, if_false = elem 80 | exp = lisp_eval(cod, env) ? if_true : if_false 81 | lisp_eval exp, env 82 | elsif elem[0] == :lambda 83 | _, params, body = elem 84 | ->(*args) { lisp_eval body, env.merge(Hash[params.zip(args)]) } 85 | elsif elem[0] == :and 86 | lisp_eval(elem[1], env) && lisp_eval(elem[2], env) 87 | elsif elem[0] == :or 88 | lisp_eval(elem[1], env) || lisp_eval(elem[2], env) 89 | elsif elem[0] == :not 90 | !lisp_eval(elem[1], env) 91 | else 92 | func, *args = elem.map { |e| lisp_eval e, env } 93 | func.call(*args) 94 | end 95 | end 96 | 97 | # (def fib (lambda (n) (if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2)))))) 98 | 99 | $copyleft = "Copyleft (Ↄ) 2021 vonhyou@lenva.tech 100 | (PRO)cessor of (L)ist for Mathematical Calculation 101 | This is an open source software, you can view its source code on github: 102 | https://github.com/vonhyou/lisp-interpreter\n\n" 103 | 104 | ##### REPL 105 | def self.repl(prompt = 'prol ƛ>> ') 106 | puts $copyleft 107 | loop do 108 | print prompt 109 | val = lisp_eval(parse(gets.chomp)) 110 | 111 | print_value val unless val.nil? || val.instance_of?(Proc) 112 | end 113 | end 114 | 115 | def self.print_value(value) 116 | puts ";Value: #{value}" 117 | end 118 | end 119 | 120 | Prolisp.repl 121 | -------------------------------------------------------------------------------- /screenshot/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vonhyou/lisp-interpreter/9efe4daa594ad84dfea26b03f7039ff4f0994fb7/screenshot/demo.png --------------------------------------------------------------------------------