fix env and write a module for futuer update

This commit is contained in:
vonhyou 2021-04-15 00:50:10 +08:00
parent 1ea52e195f
commit d0863d1636

75
prol.rb
View file

@ -9,29 +9,26 @@
# :atom # :atom
##### #####
def parse(program) module Lisp
def self.parse(program)
read_tokens(tokenize(program)) read_tokens(tokenize(program))
end end
def tokenize(program) def self.tokenize(program)
# Convert scripts to token lists # Convert scripts to token lists
program.gsub('(', ' ( ').gsub(')', ' ) ').split program.gsub('(', ' ( ').gsub(')', ' ) ').split
end end
def make_list(tokens) def self.read_tokens(tokens, lst = [])
lst = []
lst << read_tokens(tokens) while tokens[0] != ')'
tokens.shift
lst
end
def read_tokens(tokens)
# read expressions from token # read expressions from token
raise SyntaxError, 'Unexpected EOF' if tokens.empty? raise SyntaxError, 'Unexpected EOF' if tokens.empty?
case token = tokens.shift case token = tokens.shift
when '(' when '('
make_list tokens lst << read_tokens(tokens) while tokens[0] != ')'
tokens.shift
lst
when ')' when ')'
raise SyntaxError, "Unexpected ')'" raise SyntaxError, "Unexpected ')'"
else else
@ -39,7 +36,7 @@ def read_tokens(tokens)
end end
end end
def atom(token) def self.atom(token)
# Analyse numbers and symbols # Analyse numbers and symbols
case token case token
when /\d/ when /\d/
@ -51,28 +48,27 @@ end
##### Environments ##### Environments
$global_env = { def self.make_global
'+': ->(args) { args.sum }, # args.inject(0, :+) @global_env ||= begin
'-': ->(*args) { eval args.join('-') }, ops = %i[== != < <= > >= + - * /]
'*': ->(*args) { eval args.join('*') }, ops.inject({}) do |scope, op|
'/': ->(*args) { eval args.join('/') }, scope.merge op => ->(*args) { args.inject(&op) }
'>': ->(args) { args[0] > args[1] }, end
'<': ->(args) { args[0] < args[1] }, end
'=': ->(args) { args[0] == args[1] }, # @global_env.merge {
'>=': ->(args) { args[0] >= args[1] }, # 'min' : ->(*args) { args.min },
'<=': ->(args) { args[0] <= args[1] }, # 'max' : ->(*args) { args.max },
'min': ->(*args) { args.min }, # 'car' : ->(arr) { arr[0] },
'max': ->(*args) { args.max }, # 'cdr' : ->(arr) { arr[1..-1] },
'car': ->(arr) { arr[0] }, # 'cons' : ->(arr) { arr },
'cdr': ->(arr) { arr[1..-1] }, # 'quote' : ->(arr) { arr },
'cons': ->(arr) { arr }, # 'print' : ->(arg) { p arg },
'quote': ->(arr) { arr }, # 'begin' : ->(*_args) { true }
'print': ->(arg) { p arg }, # }
'begin': ->(*_args) { true } end
}
##### Lisp Eval ##### Lisp Eval
def lisp_eval(elem, env = $global_env) def self.lisp_eval(elem, env = make_global)
if elem.instance_of? Symbol if elem.instance_of? Symbol
env[elem] env[elem]
elsif elem.instance_of?(Integer) || elem.instance_of?(Float) elsif elem.instance_of?(Integer) || elem.instance_of?(Float)
@ -94,20 +90,20 @@ def lisp_eval(elem, env = $global_env)
elsif elem[0] == :not elsif elem[0] == :not
!lisp_eval(elem[1], env) !lisp_eval(elem[1], env)
else else
args = [] func, *args = elem.map { |e| lisp_eval e, env }
elem[1..-1].each { |arg| args << lisp_eval(arg, env) } func.call *args
p lisp_eval(elem[0], env)
lisp_eval(elem[0], env).call args
end end
end end
# (def fib (lambda (n) (if (<= n 2) 1 (+ (fib (- n 1)) (fib (- n 2))))))
$copyleft = "Copyleft (Ↄ) 2021 vonhyou@lenva.tech $copyleft = "Copyleft (Ↄ) 2021 vonhyou@lenva.tech
(PRO)cessor of (L)ist for Mathematical Calculation (PRO)cessor of (L)ist for Mathematical Calculation
This is an open source software, you can view its source code on github: This is an open source software, you can view its source code on github:
https://github.com/vonhyou/lisp-interpreter\n\n" https://github.com/vonhyou/lisp-interpreter\n\n"
##### REPL ##### REPL
def repl(prompt = 'prol ƛ>> ') def self.repl(prompt = 'prol ƛ>> ')
puts $copyleft puts $copyleft
loop do loop do
print prompt print prompt
@ -117,8 +113,9 @@ def repl(prompt = 'prol ƛ>> ')
end end
end end
def print_value(value) def self.print_value(value)
puts ";Value: #{value}" puts ";Value: #{value}"
end end
end
repl Lisp.repl