123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- module Chervil
- class Parser
- def initialize(lexer)
- @lexer = lexer
- @tree = Array.new
- @current_token = @lexer.get_next_token
- @error = nil
- end
-
- def parse
- until @current_token.type == :eof
- @tree << expr
- return @error unless @error.nil?
- end
- @tree
- end
-
- def expr
- if [:string, :number, :boolean].include?(@current_token.type)
- constant
- elsif @current_token.type == :identifier
- identifier
- elsif @current_token.type == :lparen
- list
- elsif @current_token.type == :quote
- quotation
- else
- @error = Error.new("Unexpected token #{@current_token.type}: #{@current_token.value}")
- end
- end
-
- def quotation
- eat(:quote)
- value = expr
- AST::Quotation.new(value)
- end
-
- def list
- eat(:lparen)
-
- if @current_token.type == :identifier
- if @current_token.value == 'define'
- return definition
- elsif @current_token.value == 'if'
- return conditional
- elsif @current_token.value == 'lambda'
- return lambda_
- end
- end
-
- elements = Array.new
- until @current_token.type == :rparen || @current_token.type == :eof
- elements << expr
- end
-
- eat(:rparen)
-
- AST::List.new(elements)
- end
-
- def definition
- eat(:identifier) # the `define`
- if @current_token.type == :identifier
- name = identifier
- value = expr
- eat(:rparen)
- AST::Definition.new(name, value)
- elsif @current_token.type == :lparen
- eat(:lparen)
- name = identifier
- params = Array.new
- until [:rparen, :eof].include?(@current_token.type)
- params << expr
- end
- eat(:rparen)
-
- body = Array.new
- until [:rparen, :eof].include?(@current_token.type)
- body << expr
- end
-
- eat(:rparen)
-
- AST::Definition.new(name, AST::Function.new(name, params, body))
- end
- end
-
- def conditional
- eat(:identifier) # the `if`
- predicate = expr
- true_branch = expr
- false_branch = expr
- eat(:rparen)
- AST::Conditional.new(predicate, true_branch, false_branch)
- end
-
- def lambda_
- eat(:identifier) # the `lambda`
-
- name = AST::Identifier.new("lambda")
- params = Array.new
-
- eat(:lparen)
- while @current_token.type == :identifier
- params.push(identifier)
- end
- eat(:rparen)
-
- body = Array.new
- until [:rparen, :eof].include?(@current_token.type)
- body << expr
- end
-
- eat(:rparen)
-
- AST::Function.new(name, params, body)
- end
-
- def identifier
- identifier = eat(:identifier)
- AST::Identifier.new(identifier.value)
- end
-
- def constant
- case @current_token.type
- when :number
- token = eat(:number)
- AST::Number.new(token.value.to_f)
- when :string
- token = eat(:string)
- AST::String.new(token.value)
- when :boolean
- token = eat(:boolean)
- AST::Boolean.new(token.value)
- end
- end
-
- def eat(type)
- if @current_token.type == type
- token = @current_token
- @current_token = @lexer.get_next_token
- token
- else
- @error = Error.new("Expected #{type} but got #{@current_token.type}")
- end
- end
- end
- end
|