関数呼び出し実装できたよー

変数と関数の区別は後ろに括弧がつくかでやってる。
可変長の引数が可能になるように作ってみた。
PostfixNotationTokens#to_infix_notationでカンマを出力できていないのでそれをなんとかしたい。

putchar( 72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, 10 )
Hello, world!
14.0
class Calc
	class Error < RuntimeError
	end
	
	class Operator
		def initialize( mark, priority, proc, is_assignment = false )
			@mark = mark
			@priority = priority
			@proc = proc
			@is_assignment = is_assignment
		end
		
		attr_reader :mark, :priority, :proc
		
		def assignment?
			@is_assignment
		end
		
		POW = new( '**', 4, proc {|a,b| a**b } )
		MUL = new( '*',  2, proc {|a,b| a*b } )
		DIV = new( '/',  2, proc {|a,b| a/b } )
		MOD = new( '%',  2, proc {|a,b| a%b } )
		ADD = new( '+',  1, proc {|a,b| a+b } )
		SUB = new( '-',  1, proc {|a,b| a-b } )
		
		SET = new( '=', 0, nil, true )
		
		ASSIGNMENTS = [POW, MUL, DIV, MOD, ADD, SUB].map {|v|
			new( v.mark + '=', 0, v.proc, true ) }
		
		INC = new( '++', 6, proc{|a| a+1 }, true )
		DEC = new( '--', 6, proc{|a| a-1 }, true )
		
		PLUS  = new( '+', 5, proc{|a| +a } )
		MINUS = new( '-', 3, proc{|a| -a } )
		
		
		IS_RIGHT_ASSOCIATIVE = { 0 => true, 4 => true }
		IS_RIGHT_ASSOCIATIVE.default = false
	end
	
	BINARY_OPERATORS = class Operator
		[POW, MUL, DIV, MOD, ADD, SUB, SET] + ASSIGNMENTS
	end.sort_by {|v| -v.mark.size }
	
	UNARY_OPERATORS = class Operator
		[INC, DEC, PLUS, MINUS]
	end.sort_by {|v| -v.mark.size }
	
	FUNCTIONS = {
		'putchar' => proc {|args| args.each {|c| putc c}; args.size.to_f },
		'rand' => proc {|args| args.empty? ? rand : rand( args[0] ).to_f }
	}
	
	class TokenType
		def initialize( regexp = nil )
			@regexp = regexp
			@prev_white_list = []
		end
		
		attr_reader :regexp
		attr_accessor :prev_white_list
		
		SPACE           = new( /\s+/ )
		BINARY_OPERATOR = new
		UNARY_OPERATOR  = new
		NUM             = new( /-?[0-9]+(?:\.[0-9]+)?/ )
		SYMBOL          = new( /(?![0-9])[0-9A-Za-z]+/ )
		LEFT_BRACKETS   = new( /\(/ )
		RIGHT_BRACKETS  = new( /\)/ )
		COMMA           = new( /,/ )
		
		VAR             = new
		FUNCTION        = new
		
		[NUM, SYMBOL, UNARY_OPERATOR, LEFT_BRACKETS].each do |i|
			i.prev_white_list = 
				[nil, LEFT_BRACKETS, BINARY_OPERATOR, UNARY_OPERATOR, COMMA]
		end
		
		[BINARY_OPERATOR, RIGHT_BRACKETS, COMMA].each do |i|
			i.prev_white_list = [NUM, SYMBOL, RIGHT_BRACKETS]
		end
		
		LEFT_BRACKETS.prev_white_list << SYMBOL
		RIGHT_BRACKETS.prev_white_list << LEFT_BRACKETS
		
		def operators
			case self
			when BINARY_OPERATOR
				BINARY_OPERATORS
			when UNARY_OPERATOR
				UNARY_OPERATORS
			end
		end
		
		def operator?
			!!operators
		end
		
		def inspect
			{ SPACE => 'SPACE', BINARY_OPERATOR => 'BINARY_OPERATOR', 
			UNARY_OPERATOR => 'UNARY_OPERATOR', NUM => 'NUM', SYMBOL => 'SYMBOL', 
			LEFT_BRACKETS => 'LEFT_BRACKETS', RIGHT_BRACKETS => 'RIGHT_BRACKETS',
			COMMA => 'COMMA', VAR => 'VAR', FUNCTION => 'FUNCTION' }[self]
		end
	end
	
	TOKEN_TYPES = class TokenType
		[SPACE, BINARY_OPERATOR, UNARY_OPERATOR,
			NUM, SYMBOL, LEFT_BRACKETS, RIGHT_BRACKETS, COMMA]
	end
	
	Token = Struct.new( :type, :val )
	
	FunctionTokenVal = Struct.new( :name, :argc )
	
	class Tokens
		def initialize
			@list = []
		end
		
		attr_accessor :list
	
		def inspect( ptr = nil )
			ret = ''
			@list.each_with_index do |t,i|
				ret += (ptr && i == ptr) ? '|' : ' '
				case t.type
				when TokenType::BINARY_OPERATOR, TokenType::UNARY_OPERATOR
					ret += t.val.mark
					if t.type == TokenType::UNARY_OPERATOR && [Operator::PLUS, Operator::MINUS].include?( t.val )
						ret += '@'
					end
				when TokenType::LEFT_BRACKETS
					ret += '('
				when TokenType::RIGHT_BRACKETS
					ret += ')'
				when TokenType::COMMA
					ret += ','
				when TokenType::FUNCTION
					ret += t.val.name
				when TokenType::VAR
					ret += t.val
				else
					ret += t.val.to_s
				end
			end
			ret += '|' if ptr && @list.size == ptr
			return ret
		end
	end
	
	class InfixNotationTokens < Tokens
		def to_postfix_notation
			dest = PostfixNotationTokens.new
			dest_ptr = 0
			prev_type = nil
			rs_stack = []
			func_stack = []
			brackets_level = 0
			rop_exit = proc do |i|
				while rop_end?( rs_stack, brackets_level, dest.list[dest_ptr], @list[i+1] )
					#puts dest.inspect( dest_ptr )
					rs_stack.pop
					if dest.list[dest_ptr] && dest.list[dest_ptr].type == TokenType::FUNCTION
						dest.list[dest_ptr].val.argc = func_stack.pop || 0
					end
					dest_ptr += 1
				end
			end
			
			@list.each_with_index do |token, i|
				case token.type
				when TokenType::NUM, TokenType::SYMBOL
					if func_stack.size > 0 && func_stack.last == nil
						func_stack[func_stack.size-1] = 1
					end
					if token.type == TokenType::SYMBOL && @list[i+1] && @list[i+1].type == TokenType::LEFT_BRACKETS
						token = Token.new( TokenType::FUNCTION, FunctionTokenVal.new( token.val ) )
						dest.list.insert( dest_ptr, token )
						rs_stack.push( brackets_level )
						func_stack.push( nil )
					else
						type = token.type
						type = TokenType::VAR if type == TokenType::SYMBOL
						dest.list.insert( dest_ptr, Token.new( type, token.val ) )
						dest_ptr += 1
						if prev_type && prev_type.operator? # 右オペランドの場合
							rop_exit.call( i )
						end
					end
				when TokenType::BINARY_OPERATOR, TokenType::UNARY_OPERATOR
					dest.list.insert( dest_ptr, token )
					rs_stack.push( brackets_level )
				when TokenType::LEFT_BRACKETS
					brackets_level += 1
				when TokenType::RIGHT_BRACKETS
					raise Error, '不正な閉じカッコ' unless brackets_level > 0
					brackets_level -= 1
					rop_exit.call( i )
				when TokenType::COMMA
					raise Error, '不正なカンマ' unless func_stack.size > 0
					func_stack[func_stack.size-1] += 1
				else
					raise Error, "不明なトークン(#{token.type.inspect})"
				end
				prev_type = token.type
				#puts dest.inspect( dest_ptr )
			end
			raise Error, '式が途中で終了している' unless dest.list.size == dest_ptr
			raise Error, 'カッコが閉じられていない' unless brackets_level == 0
			
			return dest
		end
		
		private
		# 右オペランドは終了している?
		def rop_end?( rs_stack, brackets_level, token, next_token )
			return false unless rs_stack.last == brackets_level
			
			return true unless next_token && token
			return true unless [next_token, token].all? {|v| v.type.operator? }
			next_token_priority = next_token.val.priority
			token_priority = token.val.priority
			
			if next_token_priority == token_priority && Operator::IS_RIGHT_ASSOCIATIVE[token_priority]
				return false
			end
			
			return next_token_priority <= token_priority
		end
	end
	
	class Value
		class Type
			NUM = new
			VAR = new
		end
	
		def initialize( val, val2 = nil, type = Type::NUM )
			@type = type
			@val = val
			@val2 = val2
		end
		
		def self.from_token( token, vars )
			case token.type
			when TokenType::NUM
				new( token.val )
			when TokenType::VAR
				new( token.val, vars[token.val], Type::VAR )
			end
		end
		
		attr_reader :val
		
		def value
			if @type == Type::VAR
				raise Error, '未初期化の変数を参照した' unless @val2
				@val2
			else
				@val
			end
		end
		
		def var?
			@type == Type::VAR
		end
	end
	
	class PostfixNotationTokens < Tokens
		def calculate( vars )
			stack = []
			@list.each do |token|
				case token.type
				when TokenType::NUM, TokenType::VAR
					stack.push( Value.from_token( token, vars ) )
				when TokenType::BINARY_OPERATOR
					raise Error, 'オペランドが足りない' unless stack.size >= 2
					b = stack.pop
					a = stack.pop
					if token.val.assignment?
						raise Error, '代入先が変数ではない' unless a.var?
						if token.val == Operator::SET
							stack.push( Value.new( vars[a.val] = b.value ) )
						else
							stack.push( Value.new( vars[a.val] = 
								token.val.proc.call( a.value, b.value ) ) )
						end
					else
						stack.push( Value.new( token.val.proc.call( a.value, b.value ) ) )
					end
				when TokenType::UNARY_OPERATOR
					raise Error, 'オペランドが足りない' unless stack.size >= 1
					a = stack.pop
					if token.val.assignment?
						raise Error, '代入先が変数ではない' unless a.var?
						stack.push( Value.new( vars[a.val] = token.val.proc.call( a.value ) ) )
					else
						stack.push( Value.new( token.val.proc.call( a.value ) ) )
					end
				when TokenType::FUNCTION
					raise Error, 'オペランドが足りない' unless stack.size >= token.val.argc
					args = stack[stack.size - token.val.argc, stack.size].map {|v| v.value }
					stack = stack[0,stack.size - token.val.argc]
					raise Error, '関数が見つからない' unless FUNCTIONS.key?( token.val.name )
					stack.push( Value.new( FUNCTIONS[token.val.name].call( args ) ) )
				else 
					raise Error, "不明なトークン(#{token.type.inspect})"
				end
			end

			raise Error, 'オペランドが足りないか、余っている' unless stack.size == 1
			return stack[0].value
		end
		
		def to_infix_notation
			dest = InfixNotationTokens.new
			stack = [] # 演算子が挿入されるべき位置
			@list.each do |token|
				case token.type
				when TokenType::NUM, TokenType::VAR
					dest.list << token
					stack.push( dest.list.size )
				when TokenType::BINARY_OPERATOR
					raise Error, 'オペランドが足りない' unless stack.size >= 2
					stack.pop
					pos = stack.pop
					dest.list.insert( pos, token )
					dest.list.insert( stack.last || 0, Token.new( TokenType::LEFT_BRACKETS ) )
					dest.list.insert( dest.list.size, Token.new( TokenType::RIGHT_BRACKETS ) )
					stack.push( dest.list.size )
				when TokenType::UNARY_OPERATOR
					raise Error, 'オペランドが足りない' unless stack.size >= 1
					stack.pop
					dest.list.insert( stack.last || 0, token )
					dest.list.insert( stack.last || 0, Token.new( TokenType::LEFT_BRACKETS ) )
					dest.list.insert( dest.list.size, Token.new( TokenType::RIGHT_BRACKETS ) )
					stack.push( dest.list.size )
				when TokenType::FUNCTION
					stack = stack[0,stack.size - token.val.argc]
					dest.list.insert( stack.last || 0, Token.new( TokenType::LEFT_BRACKETS ) )
					dest.list.insert( stack.last || 0, token )
					dest.list.insert( dest.list.size, Token.new( TokenType::RIGHT_BRACKETS ) )
					stack.push( dest.list.size )
				else 
					raise Error, "不明なトークン(#{token.type.inspect})"
				end
			end
			raise Error, 'オペランドが足りないか、余っている' unless stack.size == 1
			return dest
		end
	end
	
	def initialize
		@vars = {}
	end
	
	def calculate( expr )
		tokens = lex( expr )
		#p tokens
		tokens = tokens.to_postfix_notation
		#p tokens.to_infix_notation
		return tokens.calculate( @vars )
	end
	
	private
	def lex( expr )
		tokens = InfixNotationTokens.new
		i = 0
		prev_type = nil
		while expr[i]
			find = nil
			TOKEN_TYPES.each do |token_type|
				unless token_type == TokenType::SPACE
					next unless token_type.prev_white_list.include?( prev_type )
				end
				size = nil
				if operators = token_type.operators
					operator = nil
					operators.each do |o|
						next unless expr[i,o.mark.size] == o.mark
						operator = o
						find = true
						size = o.mark.size
						break
					end
				else
					find = /\A#{token_type.regexp}/.match( expr[i..-1] )
					size = find.to_s.size
				end
				next unless find
				case token_type
				when TokenType::BINARY_OPERATOR, TokenType::UNARY_OPERATOR
					tokens.list << Token.new( token_type, operator )
				when TokenType::NUM
					tokens.list << Token.new( token_type, find.to_s.to_f )
				when TokenType::SYMBOL
					tokens.list << Token.new( token_type, find.to_s )
				when TokenType::LEFT_BRACKETS, TokenType::RIGHT_BRACKETS, TokenType::COMMA
					tokens.list << Token.new( token_type )
				when TokenType::SPACE
				else
					raise Error, "不明なトークン(#{token_type.inspect})"
				end
				i += size
				prev_type = token_type unless token_type == TokenType::SPACE
				break
			end
			raise Error, '構文エラー' unless find
		end
		return tokens
	end
end

calc = Calc.new
while s = gets
	begin
		puts calc.calculate( s.chomp )
	rescue Calc::Error => e
		puts "Error: #{e}"
	end
end