Annotate Parser with grammar rules

This commit is contained in:
Charles-P. Clermont
2026-01-27 10:07:35 -05:00
parent 785427d85c
commit a4e82eb5c1
+23 -3
View File
@@ -13,6 +13,8 @@ module Liquid
@p = point @p = point
end end
# Consumes a token of specific type.
# Throws SyntaxError if token doesn't match type expectation.
def consume(type = nil) def consume(type = nil)
token = @tokens[@p] token = @tokens[@p]
if type && token[0] != type if type && token[0] != type
@@ -41,6 +43,7 @@ module Liquid
token[1] token[1]
end end
# Peeks the ahead token, returning true if matching expectation
def look(type, ahead = 0) def look(type, ahead = 0)
tok = @tokens[@p + ahead] tok = @tokens[@p + ahead]
return false unless tok return false unless tok
@@ -51,7 +54,7 @@ module Liquid
# logical := equality (("and" | "or") equality)* # logical := equality (("and" | "or") equality)*
# equality := comparison (("==" | "!=" | "<>") comparison)* # equality := comparison (("==" | "!=" | "<>") comparison)*
# comparison := primary ((">=" | ">" | "<" | "<=" | ... ) primary)* # comparison := primary ((">=" | ">" | "<" | "<=" | ... ) primary)*
# primary := string | number | variable_lookup | range | boolean # primary := string | number | variable_lookup | range | boolean | grouping
def expression def expression
logical logical
end end
@@ -60,7 +63,8 @@ module Liquid
# associative. This creates a right-leaning tree and is why the method # associative. This creates a right-leaning tree and is why the method
# looks a bit more complicated # looks a bit more complicated
# #
# `a == b and b or c` is evaluated like (a and (b or c)) # `a and b or c` is evaluated like (a and (b or c))
# logical := equality (("and" | "or") equality)*
def logical def logical
operator = nil operator = nil
expr = equality expr = equality
@@ -69,6 +73,7 @@ module Liquid
expr expr
end end
# equality := comparison (("==" | "!=" | "<>") comparison)*
def equality def equality
expr = comparison expr = comparison
while look(:equality) while look(:equality)
@@ -88,11 +93,12 @@ module Liquid
expr expr
end end
# primary := string | number | variable_lookup | range | boolean | grouping
def primary def primary
token = @tokens[@p] token = @tokens[@p]
case token[0] case token[0]
when :id when :id
variable_lookup variable_lookup_or_literal
when :open_square when :open_square
unnamed_variable_lookup unnamed_variable_lookup
when :string when :string
@@ -115,7 +121,18 @@ module Liquid
consume(:string)[1..-2] consume(:string)[1..-2]
end end
# variable_lookup := id (lookup)*
# lookup := indexed_lookup | dot_lookup
# indexed_lookup := "[" expression "]"
# dot_lookup := "." id
def variable_lookup def variable_lookup
name = consume(:id)
lookups, command_flags = variable_lookups
VariableLookup.new(name, lookups, command_flags)
end
# a variable_lookup without lookups could be a literal
def variable_lookup_or_literal
name = consume(:id) name = consume(:id)
lookups, command_flags = variable_lookups lookups, command_flags = variable_lookups
if Expression::LITERALS.key?(name) && lookups.empty? if Expression::LITERALS.key?(name) && lookups.empty?
@@ -125,6 +142,7 @@ module Liquid
end end
end end
# unnamed_variable_lookup := indexed_lookup (lookup)*
def unnamed_variable_lookup def unnamed_variable_lookup
name = indexed_lookup name = indexed_lookup
lookups, command_flags = variable_lookups lookups, command_flags = variable_lookups
@@ -132,6 +150,7 @@ module Liquid
end end
# Parenthesized expressions are recursive # Parenthesized expressions are recursive
# grouping := "(" expression ")"
def grouping_or_range_lookup def grouping_or_range_lookup
consume(:open_round) consume(:open_round)
expr = expression expr = expression
@@ -144,6 +163,7 @@ module Liquid
consume(:close_round) consume(:close_round)
end end
# range_lookup := "(" expression ".." expression ")"
def range_lookup def range_lookup
consume(:open_round) consume(:open_round)
first = expression first = expression