Class: CFG::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/CFG/config.rb

Constant Summary collapse

EXPRESSION_STARTERS =
VALUE_STARTERS =
COMPARISON_OPERATORS =

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream) ⇒ Parser

Returns a new instance of Parser.



846
847
848
849
# File 'lib/CFG/config.rb', line 846

def initialize(stream)
  @tokenizer = Tokenizer.new stream
  @next_token = @tokenizer.get_token
end

Instance Attribute Details

#next_tokenObject (readonly)

Returns the value of attribute next_token.



844
845
846
# File 'lib/CFG/config.rb', line 844

def next_token
  @next_token
end

#tokenizerObject (readonly)

Returns the value of attribute tokenizer.



843
844
845
# File 'lib/CFG/config.rb', line 843

def tokenizer
  @tokenizer
end

Instance Method Details

#_get_slice_elementObject



969
970
971
972
973
974
975
# File 'lib/CFG/config.rb', line 969

def _get_slice_element
  lb = list_body
  size = lb.elements.length

  _invalid_index(size, lb.start) unless size == 1
  lb.elements[0]
end

#_invalid_index(num, pos) ⇒ Object



963
964
965
966
967
# File 'lib/CFG/config.rb', line 963

def _invalid_index(num, pos)
  e = ParserError.new "Invalid index at #{pos}: expected 1 expression, found #{num}"
  e.location = pos
  raise e
end

#_try_get_stepObject



977
978
979
980
# File 'lib/CFG/config.rb', line 977

def _try_get_step
  kind = advance
  kind == :RIGHT_BRACKET ? nil : _get_slice_element
end

#add_exprObject



1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
# File 'lib/CFG/config.rb', line 1174

def add_expr
  result = mul_expr
  kind = @next_token.kind

  while %i[PLUS MINUS].include? kind
    advance
    result = BinaryNode.new kind, result, mul_expr
    kind = @next_token.kind
  end
  result
end

#advanceObject



855
856
857
858
# File 'lib/CFG/config.rb', line 855

def advance
  @next_token = @tokenizer.get_token
  @next_token.kind
end

#and_exprObject



1266
1267
1268
1269
1270
1271
1272
1273
# File 'lib/CFG/config.rb', line 1266

def and_expr
  result = not_expr
  while @next_token.kind == :AND
    advance
    result = BinaryNode.new :AND, result, not_expr
  end
  result
end

#at_endObject



851
852
853
# File 'lib/CFG/config.rb', line 851

def at_end
  @next_token.kind == :EOF
end

#atomObject



935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
# File 'lib/CFG/config.rb', line 935

def atom
  kind = @next_token.kind
  case kind
  when :LEFT_CURLY
    result = mapping
  when :LEFT_BRACKET
    result = list
  when :DOLLAR
    expect :DOLLAR
    expect :LEFT_CURLY
    spos = @next_token.start
    result = UnaryNode.new :DOLLAR, primary
    result.start = spos
    expect :RIGHT_CURLY
  when :WORD, :INTEGER, :FLOAT, :COMPLEX, :STRING, :BACKTICK, :TRUE, :FALSE, :NONE
    result = value
  when :LEFT_PARENTHESIS
    expect :LEFT_PARENTHESIS
    result = expr
    expect :RIGHT_PARENTHESIS
  else
    e = ParserError.new "Unexpected: #{kind}"
    e.location = @next_token.start
    raise e
  end
  result
end

#bitand_exprObject



1198
1199
1200
1201
1202
1203
1204
1205
1206
# File 'lib/CFG/config.rb', line 1198

def bitand_expr
  result = shift_expr

  while @next_token.kind == :BITWISE_AND
    advance
    result = BinaryNode.new :BITWISE_AND, result, shift_expr
  end
  result
end

#bitor_exprObject



1218
1219
1220
1221
1222
1223
1224
1225
1226
# File 'lib/CFG/config.rb', line 1218

def bitor_expr
  result = bitxor_expr

  while @next_token.kind == :BITWISE_OR
    advance
    result = BinaryNode.new :BITWISE_OR, result, bitxor_expr
  end
  result
end

#bitxor_exprObject



1208
1209
1210
1211
1212
1213
1214
1215
1216
# File 'lib/CFG/config.rb', line 1208

def bitxor_expr
  result = bitand_expr

  while @next_token.kind == :BITWISE_XOR
    advance
    result = BinaryNode.new :BITWISE_XOR, result, bitand_expr
  end
  result
end

#comp_opObject



1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
# File 'lib/CFG/config.rb', line 1228

def comp_op
  result = @next_token.kind
  should_advance = false
  advance
  if result == :IS && @next_token.kind == :NOT
    result = :IS_NOT
    should_advance = true
  elsif result == :NOT && @next_token.kind == :IN
    result = :NOT_IN
    should_advance = true
  end
  advance if should_advance
  result
end

#comparisonObject



1248
1249
1250
1251
1252
1253
1254
1255
# File 'lib/CFG/config.rb', line 1248

def comparison
  result = bitor_expr
  while COMPARISON_OPERATORS.include? @next_token.kind
    op = comp_op
    result = BinaryNode.new op, result, bitor_expr
  end
  result
end

#consume_newlinesObject



872
873
874
875
876
877
# File 'lib/CFG/config.rb', line 872

def consume_newlines
  result = @next_token.kind

  result = advance while result == :NEWLINE
  result
end

#containerObject



1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
# File 'lib/CFG/config.rb', line 1120

def container
  kind = consume_newlines

  case kind
  when :LEFT_CURLY
    result = mapping
  when :LEFT_BRACKET
    result = list
  when :WORD, :STRING, :EOF
    result = mapping_body
  else
    e = ParserError.new "Unexpected type for container: #{kind}"

    e.location = @next_token.start
    raise e
  end
  consume_newlines
  result
end

#expect(kind) ⇒ Object



860
861
862
863
864
865
866
867
868
869
870
# File 'lib/CFG/config.rb', line 860

def expect(kind)
  if @next_token.kind != kind
    e = ParserError.new "Expected #{kind} but got #{@next_token.kind}"
    e.location = @next_token.start
    # require 'byebug'; byebug
    raise e
  end
  result = @next_token
  advance
  result
end

#exprObject



1275
1276
1277
1278
1279
1280
1281
1282
# File 'lib/CFG/config.rb', line 1275

def expr
  result = and_expr
  while @next_token.kind == :OR
    advance
    result = BinaryNode.new :OR, result, and_expr
  end
  result
end

#listObject



1113
1114
1115
1116
1117
1118
# File 'lib/CFG/config.rb', line 1113

def list
  expect :LEFT_BRACKET
  result = list_body
  expect :RIGHT_BRACKET
  result
end

#list_bodyObject



1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
# File 'lib/CFG/config.rb', line 1096

def list_body
  result = []
  kind = consume_newlines
  spos = @next_token.start
  while EXPRESSION_STARTERS.include? kind
    result.push(expr)
    kind = @next_token.kind
    break unless %i[NEWLINE COMMA].include? kind

    advance
    kind = consume_newlines
  end
  result = ListNode.new result
  result.start = spos
  result
end

#mappingObject



1089
1090
1091
1092
1093
1094
# File 'lib/CFG/config.rb', line 1089

def mapping
  expect :LEFT_CURLY
  result = mapping_body
  expect :RIGHT_CURLY
  result
end

#mapping_bodyObject



1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
# File 'lib/CFG/config.rb', line 1054

def mapping_body
  result = []
  kind = consume_newlines
  spos = @next_token.start
  if kind != :RIGHT_CURLY && kind != :EOF
    if kind != :WORD && kind != :STRING
      e = ParserError.new "Unexpected type for key: #{kind}"

      e.location = @next_token.start
      raise e
    end
    while %i[WORD STRING].include? kind
      key = object_key
      kind = @next_token.kind
      if kind != :COLON && kind != :ASSIGN
        e = ParserError.new "Expected key-value separator, found: #{kind}"

        e.location = @next_token.start
        raise e
      end
      advance
      consume_newlines
      result.push [key, expr]
      kind = @next_token.kind
      if %i[NEWLINE COMMA].include? kind
        advance
        kind = consume_newlines
      end
    end
  end
  result = MappingNode.new result
  result.start = spos
  result
end

#mul_exprObject



1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
# File 'lib/CFG/config.rb', line 1162

def mul_expr
  result = unary_expr
  kind = @next_token.kind

  while %i[STAR SLASH SLASH_SLASH MODULO].include? kind
    advance
    result = BinaryNode.new kind, result, unary_expr
    kind = @next_token.kind
  end
  result
end

#not_exprObject



1257
1258
1259
1260
1261
1262
1263
1264
# File 'lib/CFG/config.rb', line 1257

def not_expr
  if @next_token.kind != :NOT
    comparison
  else
    advance
    UnaryNode.new :NOT, not_expr
  end
end

#object_keyObject



1044
1045
1046
1047
1048
1049
1050
1051
1052
# File 'lib/CFG/config.rb', line 1044

def object_key
  if @next_token.kind == :STRING
    result = strings
  else
    result = @next_token
    advance
  end
  result
end

#powerObject



1140
1141
1142
1143
1144
1145
1146
1147
# File 'lib/CFG/config.rb', line 1140

def power
  result = primary
  while @next_token.kind == :POWER
    advance
    result = BinaryNode.new :POWER, result, unary_expr
  end
  result
end

#primaryObject



1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
# File 'lib/CFG/config.rb', line 1033

def primary
  result = atom
  kind = @next_token.kind
  while %i[DOT LEFT_BRACKET].include? kind
    op, rhs = trailer
    result = BinaryNode.new op, result, rhs
    kind = @next_token.kind
  end
  result
end

#shift_exprObject



1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
# File 'lib/CFG/config.rb', line 1186

def shift_expr
  result = add_expr
  kind = @next_token.kind

  while %i[LEFT_SHIFT RIGHT_SHIFT].include? kind
    advance
    result = BinaryNode.new kind, result, add_expr
    kind = @next_token.kind
  end
  result
end

#stringsObject



891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
# File 'lib/CFG/config.rb', line 891

def strings
  result = @next_token
  if advance == :STRING
    all_text = ''
    all_value = ''
    t = result.text
    v = result.value
    start = result.start
    endpos = result.end

    loop do
      all_text += t
      all_value += v
      t = @next_token.text
      v = @next_token.value
      endpos = @next_token.end
      kind = advance
      break if kind != :STRING
    end
    all_text += t # the last one
    all_value += v
    result = Token.new :STRING, all_text, all_value
    result.start = start
    result.end = endpos
  end
  result
end

#trailerObject



982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
# File 'lib/CFG/config.rb', line 982

def trailer
  op = @next_token.kind

  if op != :LEFT_BRACKET
    expect :DOT
    result = expect :WORD
  else
    kind = advance
    is_slice = false
    start_index = nil
    stop_index = nil
    step = nil

    if kind == :COLON
      # it's a slice like [:xyz:abc]
      is_slice = true
    else
      elem = _get_slice_element

      kind = @next_token.kind
      if kind != :COLON
        result = elem
      else
        start_index = elem
        is_slice = true
      end
    end
    if is_slice
      op = :COLON
      # at this point startIndex is either nil (if foo[:xyz]) or a
      # value representing the start. We are pointing at the COLON
      # after the start value
      kind = advance
      if kind == :COLON # no stop, but there might be a step
        s = _try_get_step
        step = s unless s.nil?
      elsif kind != :RIGHT_BRACKET
        stop_index = _get_slice_element
        kind = @next_token.kind
        if kind == :COLON
          s = _try_get_step
          step = s unless s.nil?
        end
      end
      result = SliceNode.new start_index, stop_index, step
    end
    expect :RIGHT_BRACKET
  end
  [op, result]
end

#unary_exprObject



1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
# File 'lib/CFG/config.rb', line 1149

def unary_expr
  kind = @next_token.kind
  spos = @next_token.start
  result = if !%i[PLUS MINUS BITWISE_COMPLEMENT AT].include? kind
             power
           else
             advance
             UnaryNode.new kind, unary_expr
           end
  result.start = spos
  result
end

#valueObject



919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
# File 'lib/CFG/config.rb', line 919

def value
  kind = @next_token.kind
  unless VALUE_STARTERS.include? kind
    e = ParserError.new "Unexpected when looking for value: #{kind}"
    e.location = @next_token.start
    raise e
  end
  if kind == :STRING
    result = strings
  else
    result = @next_token
    advance
  end
  result
end