require "compiler/crystal/syntax"
s = "a = 5; puts a + 3"
nodes = Crystal::Parser.parse(s)
puts nodes.class # => Crystal::Expressions
puts nodes.is_a? Crystal::Expressions # => true
puts nodes.is_a? Crystal::Nop # => false
puts nodes.expressions
So, I would assume the last expression to give an Array (or an ArrayLiteral node). However, I get
undefined method 'expressions' for Crystal::Nop (compile-time type is Crystal::ASTNode+)
Which makes no sense. .class
and .is_a?
are runtime checks, so the nodes
, which has a compile-time type of ASTNode
should be Crystal::Expressions
, not Crystal::Nop
.
The behaviour is the same on crystal versions 0.25.0, 0.25.1, 0.26.0, 0.26.1 and the version currently on the master branch of the git repo.
The error is raised at compile time because not all subclasses of Crystal::ASTNode
have #expressions
method. Crystal::Nop
just happens to be the first such subclass the compiler checks and subsequently decides to throw an error. The way to fix the code is:
require "compiler/crystal/syntax"
s = "a = 5; puts a + 3"
nodes = Crystal::Parser.parse(s)
case nodes
when Crystal::Expressions
puts nodes.expressions
when Crystal::Nop
puts "It's a nop"
else
puts "Unhandles case for #{nodes.class}"
end
Alternatively, you can force the compiler to assume it's Crystal::Expressions
by using .as
:
nodes.as(Crystal::Expressions).expressions
Credit for this answer goes to straight-shoota on this Github issue