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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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. application
  23. else
  24. @error = Error.new("Unexpected token #{@current_token.type}: #{@current_token.value}")
  25. end
  26. end
  27. def application
  28. eat(:lparen)
  29. if @current_token.type == :identifier
  30. if @current_token.value == 'define'
  31. return definition
  32. elsif @current_token.value == 'if'
  33. return conditional
  34. end
  35. end
  36. expression = expr
  37. args = Array.new
  38. until @current_token.type == :rparen || @current_token.type == :eof
  39. args << expr
  40. end
  41. eat(:rparen)
  42. AST::Application.new(expression, args)
  43. end
  44. def definition
  45. eat(:identifier) # the `define`
  46. if @current_token.type == :identifier
  47. name = identifier
  48. value = expr
  49. eat(:rparen)
  50. AST::Definition.new(name, value)
  51. elsif @current_token.type == :lparen
  52. eat(:lparen)
  53. name = identifier
  54. params = Array.new
  55. until [:rparen, :eof].include?(@current_token.type)
  56. params << expr
  57. end
  58. eat(:rparen)
  59. body = Array.new
  60. until [:rparen, :eof].include?(@current_token.type)
  61. body << expr
  62. end
  63. eat(:rparen)
  64. AST::Definition.new(name, AST::Function.new(name, params, body))
  65. end
  66. end
  67. def conditional
  68. eat(:identifier) # the `if`
  69. predicate = expr
  70. true_branch = expr
  71. false_branch = expr
  72. eat(:rparen)
  73. AST::Conditional.new(predicate, true_branch, false_branch)
  74. end
  75. def identifier
  76. identifier = eat(:identifier)
  77. AST::Identifier.new(identifier.value)
  78. end
  79. def constant
  80. case @current_token.type
  81. when :number
  82. token = eat(:number)
  83. AST::Number.new(token.value.to_f)
  84. when :string
  85. token = eat(:string)
  86. AST::String.new(token.value)
  87. when :boolean
  88. token = eat(:boolean)
  89. AST::Boolean.new(token.value)
  90. end
  91. end
  92. def eat(type)
  93. if @current_token.type == type
  94. token = @current_token
  95. @current_token = @lexer.get_next_token
  96. token
  97. else
  98. @error = Error.new("Expected #{type} but got #{@current_token.type}")
  99. end
  100. end
  101. end
  102. end