Browse Source

Improve parsing; Value types;

bjorn 9 years ago
parent
commit
3201758a91
1 changed files with 139 additions and 11 deletions
  1. 139 11
      ast.lua

+ 139 - 11
ast.lua

@@ -1,26 +1,154 @@
 local lpeg = require 'lpeg'
-local P, R, S, V, C, Ct, Cmt, Cg = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cmt, lpeg.Cg
+local P, R, S, V, C, Ct, Cmt, Cg, Cc, Cf = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Cmt, lpeg.Cg, lpeg.Cc, lpeg.Cf
+
+local function pack(...)
+  return { n = select('#', ...), ... }
+end
 
 -- Utility
 local space = S(' \t\r\n') ^ 0
-local comma = P(',') ^ -1
+local comma = P(',') ^ 0
+
+local function cName(name)
+  if #name == 0 then return nil end
+
+  return {
+    kind = 'name',
+    value = name
+  }
+end
+
+local function cInt(value)
+  return {
+    kind = 'int',
+    value = value
+  }
+end
+
+local function cFloat(value)
+  return {
+    kind = 'float',
+    value = value
+  }
+end
+
+local function cBoolean(value)
+  return {
+    kind = 'boolean',
+    value = value
+  }
+end
+
+local function cString(value)
+  return {
+    kind = 'string',
+    value = value
+  }
+end
+
+local function cEnum(value)
+  return {
+    kind = 'enum',
+    value = value
+  }
+end
+
+local function cList(value)
+  return {
+    kind = 'list',
+    values = value
+  }
+end
+
+local function cObjectField(name, value)
+  print(name, value)
+  return {
+    name = name,
+    value = value
+  }
+end
+
+local function cObject(fields)
+  return {
+    kind = 'object',
+    values = fields
+  }
+end
+
+local function cAlias(name)
+  return {
+    kind = 'alias',
+    name = name
+  }
+end
+
+local function cArgument(name, value)
+  return {
+    kind = 'argument',
+    name = name,
+    value = value
+  }
+end
+
+local function cField(...)
+  local tokens = pack(...)
+  local field = { kind = 'field' }
+
+  for i = 1, #tokens do
+    local key = tokens[i].kind
+    if not key and tokens[i][1].kind == 'argument' then
+      key = 'arguments'
+    end
+
+    field[key] = tokens[i]
+  end
+
+  return field
+end
+
+local function cSelectionSet(selections)
+  return {
+    kind = 'selectionSet',
+    selections = selections
+  }
+end
+
+local function cFragmentSpread(name)
+  if name == 'on' then error('Fragment name cannot be "on"') end
+
+  return {
+    kind = 'fragmentSpread',
+    name = name
+  }
+end
 
 -- "Terminals"
-local name = space * C(R('az', 'AZ') * (P('_') + R('09') + R('az', 'AZ')) ^ 0)
-local alias = space * name * P(':')
-local value = space * C(R('09') ^ 1) -- todo values are hard
-local argument = space * Ct(Cg(name, 'name') * P(':') * Cg(value, 'value')) * comma
-local arguments = P('(') * Ct(argument ^ 1) * P(')')
-local fragmentName = space * (name - 'on')
-local fragmentSpread = space * P('...') * fragmentName
+local rawName = space * R('az', 'AZ') * (P('_') + R('09') + R('az', 'AZ')) ^ 0
+local name = rawName / cName
+local alias = space * name * P(':') / cAlias
+local integerPart = P('-') ^ -1 * (P('0') + R('19') * R('09') ^ 0)
+local intValue = integerPart / cInt
+local fractionalPart = P('.') * R('09') ^ 1
+local exponentialPart = S('Ee') * S('+-') ^ -1 * R('09') ^ 1
+local floatValue = integerPart * (fractionalPart ^ -1 * exponentialPart ^ -1) / cFloat
+local booleanValue = (P('true') + P('false')) / cBoolean
+local stringValue = P('"') * C((P('\\"') + 1 - S('"\n')) ^ 0) * P('"') / cString
+local enumValue = (rawName - 'true' - 'false' - 'null') / cEnum
+local fragmentSpread = space * P('...') * name / cFragmentSpread
 
 -- Nonterminals
 local graphQL = P {
   'input',
   input = space * V('selectionSet') * -1,
-  selectionSet = space * P('{') * space * Ct(V('selection') ^ 0) * space * P('}'),
+  selectionSet = space * P('{') * space * Ct(V('selection') ^ 0) * space * P('}') / cSelectionSet,
   selection = space * (V('field') + fragmentSpread),
-  field = Ct(space * Cg(alias ^ -1, 'alias') * Cg(name, 'name') * Cg(arguments ^ -1, 'arguments') * Cg(V('selectionSet'), 'children') ^ 0) * comma,
+  field = space * alias ^ -1 * name * V('arguments') ^ -1 * V('selectionSet') ^ -1 * comma / cField,
+  argument = space * name * P(':') * V('value') * comma / cArgument,
+  arguments = P('(') * Ct(V('argument') ^ 1) * P(')'),
+  value = space * (V('objectValue') + V('listValue') + enumValue + stringValue + booleanValue + floatValue + intValue),
+  listValue = P('[') * Ct((V('value') * comma) ^ 0) * P(']') / cList,
+  objectFieldValue = C(rawName) * space * P(':') * space * V('value') * comma / cObjectField,
+  objectValue = P('{') * space * Ct(V('objectFieldValue') ^ 0) * space * P('}') / cObject
 }
 
 return function(str)