From 478e6e4e5d73fc753038f5c5c5203d8bd2835026 Mon Sep 17 00:00:00 2001 From: vonhyou Date: Mon, 12 Apr 2021 19:52:57 +0800 Subject: [PATCH] finish calucator --- minlisp.rb | 97 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 84 insertions(+), 13 deletions(-) diff --git a/minlisp.rb b/minlisp.rb index f7aa315..0cedb49 100644 --- a/minlisp.rb +++ b/minlisp.rb @@ -2,6 +2,17 @@ # Author: @vonhyou # Start at: Apr. 10, 2021 +##### Parser +# :parse +# :tokenize +# :read_tokens +# :atom +##### + +def parse(program) + read_tokens(tokenize(program)) +end + def tokenize(program) # Convert scripts to token lists replacements = { '(' => ' ( ', ')' => ' ) ' } @@ -9,30 +20,90 @@ def tokenize(program) .split(' ') end +def make_list(tokens) + lst = [] + lst << read_tokens(tokens) while tokens[0] != ')' + tokens.shift + lst +end + def read_tokens(tokens) # read expressions from token raise SyntaxError, 'Unexpected EOF' if tokens.empty? token = tokens.shift - if token == '(' - lst = [] - lst << read_tokens(tokens) while tokens[0] != ')' - tokens.shift - return lst - elsif token == ')' + case token + when '(' + make_list tokens + when ')' raise SyntaxError, "Unexpected ')'" else - return atom token + atom token end end -def isInteger?(atom) = atom.match?(/^-?\d+$/) -def isFloat?(atom) = atom.match?(/^(-?\d+)(\.\d+)?$/) - def atom(token) # Analyse numbers and symbols - return Integer token if isInteger? token - return Float token if isFloat? token - token + isInteger = ->(atom) { atom.match?(/^-?\d+$/) } + isFloat = ->(atom) { atom.match?(/^(-?\d+)(\.\d+)?$/) } + return Integer token if isInteger.call token + return Float token if isFloat.call token + + token.to_sym 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)')