finish calucator

This commit is contained in:
vonhyou 2021-04-12 19:52:57 +08:00
parent 4f7bbe8129
commit 478e6e4e5d

View file

@ -2,6 +2,17 @@
# Author: @vonhyou # Author: @vonhyou
# Start at: Apr. 10, 2021 # Start at: Apr. 10, 2021
##### Parser
# :parse
# :tokenize
# :read_tokens
# :atom
#####
def parse(program)
read_tokens(tokenize(program))
end
def tokenize(program) def tokenize(program)
# Convert scripts to token lists # Convert scripts to token lists
replacements = { '(' => ' ( ', ')' => ' ) ' } replacements = { '(' => ' ( ', ')' => ' ) ' }
@ -9,30 +20,90 @@ def tokenize(program)
.split(' ') .split(' ')
end end
def make_list(tokens)
lst = []
lst << read_tokens(tokens) while tokens[0] != ')'
tokens.shift
lst
end
def read_tokens(tokens) 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?
token = tokens.shift token = tokens.shift
if token == '(' case token
lst = [] when '('
lst << read_tokens(tokens) while tokens[0] != ')' make_list tokens
tokens.shift when ')'
return lst
elsif token == ')'
raise SyntaxError, "Unexpected ')'" raise SyntaxError, "Unexpected ')'"
else else
return atom token atom token
end end
end end
def isInteger?(atom) = atom.match?(/^-?\d+$/)
def isFloat?(atom) = atom.match?(/^(-?\d+)(\.\d+)?$/)
def atom(token) def atom(token)
# Analyse numbers and symbols # Analyse numbers and symbols
return Integer token if isInteger? token isInteger = ->(atom) { atom.match?(/^-?\d+$/) }
return Float token if isFloat? token isFloat = ->(atom) { atom.match?(/^(-?\d+)(\.\d+)?$/) }
token return Integer token if isInteger.call token
return Float token if isFloat.call token
token.to_sym
end end
# p parse '(def 1 2 (c 3.3 (r f r) e))'
##### Environments
def generate_env
lisp_env = {
'+': ->(arr) { arr.sum }, # args.inject(0, :+)
'-': ->(*args) { eval args.join('-') },
'*': ->(*args) { eval args.join('*') },
'/': ->(*args) { eval args.join('/') },
'>': ->(x, y) { x > y },
'<': ->(x, y) { x < y },
'=': ->(x, y) { x == y },
'>=': ->(x, y) { x >= y },
'<=': ->(x, y) { x <= y },
'min': ->(*args) { args.min },
'max': ->(*args) { args.max },
'car': ->(arr) { arr[0] },
'cdr': ->(arr) { arr[1..-1] }
}
end
# puts lisp_env[:+].call 1, 2, 3
# puts lisp_env[:-].call 1, 2, 3
# puts lisp_env[:*].call 2, 3, 4
# puts lisp_env[:/].call 9, 5, 1
# puts lisp_env[:>].call 1, 2
# p lisp_env[:car].call [1, 2, 3]
# p lisp_env[:cdr].call [1, 2, 3]
def do_sth ; end
##### Lisp Eval
def lisp_eval(elem, env = generate_env)
if elem.instance_of?(Symbol)
env[elem]
elsif elem.instance_of?(Integer) || elem.instance_of?(Float)
elem
elsif elem[0] == :def
do_sth
elsif elem[0] == :if
do_sth
else
args = []
elem[1..-1].each { |arg| args << lisp_eval(arg, env) }
p lisp_eval(elem[0], env)
lisp_eval(elem[0], env).call args
end
end
p lisp_eval(parse '(/ (+ 1 (* 2 3) 1 1 (+ 1 (- 7 2) 1)) 4)')