Chervil is a toy Lisp interpreter written in Ruby
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

parser.rb 3.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. module Chervil
  2. class Parser
  3. def initialize(lexer)
  4. @lexer = lexer
  5. @tree = Array.new
  6. @current_token = @lexer.get_next_token
  7. @error = nil
  8. end
  9. def parse
  10. until @current_token.type == :eof
  11. @tree << expr
  12. return @error unless @error.nil?
  13. end
  14. @tree
  15. end
  16. def expr
  17. if [:string, :number, :boolean].include?(@current_token.type)
  18. constant
  19. elsif @current_token.type == :identifier
  20. identifier
  21. elsif @current_token.type == :lparen
  22. list
  23. elsif @current_token.type == :quote
  24. quotation
  25. else
  26. @error = Error.new("Unexpected token #{@current_token.type}: #{@current_token.value}")
  27. end
  28. end
  29. def quotation
  30. eat(:quote)
  31. value = expr
  32. AST::Quotation.new(value)
  33. end
  34. def list
  35. eat(:lparen)
  36. if @current_token.type == :identifier
  37. if @current_token.value == 'define'
  38. return definition
  39. elsif @current_token.value == 'if'
  40. return conditional
  41. elsif @current_token.value == 'lambda'
  42. return lambda_
  43. end
  44. end
  45. elements = Array.new
  46. until @current_token.type == :rparen || @current_token.type == :eof
  47. elements << expr
  48. end
  49. eat(:rparen)
  50. AST::List.new(elements)
  51. end
  52. def definition
  53. eat(:identifier) # the `define`
  54. if @current_token.type == :identifier
  55. name = identifier
  56. value = expr
  57. eat(:rparen)
  58. AST::Definition.new(name, value)
  59. elsif @current_token.type == :lparen
  60. eat(:lparen)
  61. name = identifier
  62. params = Array.new
  63. until [:rparen, :eof].include?(@current_token.type)
  64. params << expr
  65. end
  66. eat(:rparen)
  67. body = Array.new
  68. until [:rparen, :eof].include?(@current_token.type)
  69. body << expr
  70. end
  71. eat(:rparen)
  72. AST::Definition.new(name, AST::Function.new(name, params, body))
  73. end
  74. end
  75. def conditional
  76. eat(:identifier) # the `if`
  77. predicate = expr
  78. true_branch = expr
  79. false_branch = expr
  80. eat(:rparen)
  81. AST::Conditional.new(predicate, true_branch, false_branch)
  82. end
  83. def lambda_
  84. eat(:identifier) # the `lambda`
  85. name = AST::Identifier.new("lambda")
  86. params = Array.new
  87. eat(:lparen)
  88. while @current_token.type == :identifier
  89. params.push(identifier)
  90. end
  91. eat(:rparen)
  92. body = Array.new
  93. until [:rparen, :eof].include?(@current_token.type)
  94. body << expr
  95. end
  96. eat(:rparen)
  97. AST::Function.new(name, params, body)
  98. end
  99. def identifier
  100. identifier = eat(:identifier)
  101. AST::Identifier.new(identifier.value)
  102. end
  103. def constant
  104. case @current_token.type
  105. when :number
  106. token = eat(:number)
  107. AST::Number.new(token.value.to_f)
  108. when :string
  109. token = eat(:string)
  110. AST::String.new(token.value)
  111. when :boolean
  112. token = eat(:boolean)
  113. AST::Boolean.new(token.value)
  114. end
  115. end
  116. def eat(type)
  117. if @current_token.type == type
  118. token = @current_token
  119. @current_token = @lexer.get_next_token
  120. token
  121. else
  122. @error = Error.new("Expected #{type} but got #{@current_token.type}")
  123. end
  124. end
  125. end
  126. end