0

I wrote a very simple parser combinator library which seems to work alright (https://github.com/mukeshsoni/tinyparsec).

I then tried writing parser for json with the library. The code for the json parser is here - https://github.com/mukeshsoni/tinyparsec/blob/master/src/example_parsers/JsonParser.purs

The grammar for json is recursive -

data JsonVal
    = JsonInt Int
    | JsonString String
    | JsonBool Boolean
    | JsonObj (List (Tuple String JsonVal))

Which means the parser for json object must again call the parser for jsonVal. The code for jsonObj parser looks like this -

jsonValParser
    = jsonIntParser <|> jsonBoolParser <|> jsonStringParser <|> jsonObjParser

propValParser :: Parser (Tuple String JsonVal)
propValParser = do
    prop <- stringLitParser
    _ <- symb ":"
    val <- jsonValParser
    pure (Tuple prop val)

listOfPropValParser :: Parser (List (Tuple String JsonVal))
listOfPropValParser = sepBy propValParser (symb ",")


jsonObjParser :: Parser JsonVal
jsonObjParser = do
    _ <- symb "{"
    propValList <- listOfPropValParser
    _ <- symb "}"
    pure (JsonObj propValList)

But when i try to build it, i get the following error - The value of propValParser is undefined here. So this reference is not allowed here

I found similar issues on stackoverflow but could not understand why the error happens or how should i refactor my code so that it takes care of the recursive references from jsonValParser to propValParser.

Any help would be appreciated.

Mukesh Soni
  • 6,646
  • 3
  • 30
  • 37
  • Possible duplicate of ["undefined value, reference not allowed" workaround](http://stackoverflow.com/questions/36984245/undefined-value-reference-not-allowed-workaround) – Prune Apr 19 '17 at 15:58
  • Possible, but I thought the code looked different. Also, I didn't understand the reason behind that pattern not being allowed. – Mukesh Soni Apr 19 '17 at 17:24

2 Answers2

1

See https://stackoverflow.com/a/36991223/139614 for a similar case - you'll need to make use of the fix function, or introduce Unit -> ... in front of a parser somewhere to break the cyclic definition.

Community
  • 1
  • 1
gb.
  • 4,629
  • 1
  • 20
  • 19
  • What does `fix` do? How does it break the cycle? Why is cyclic definition a problem? I have another example of cyclic definition in my parser library (for defining `many` and `many1` combinators - https://github.com/mukeshsoni/tinyparsec/blob/master/src/TinyParsec.purs#L80). Why is that not a problem? – Mukesh Soni Apr 19 '17 at 10:52
0

I managed to get rid of the error by wrapping the blocks which were throwing error inside a do block and starting the do block with a noop -

listOfPropValParser :: Parser (List (Tuple String JsonVal))
listOfPropValParser = do
    _ <- pure 1 -- does nothing but defer the execution of the second line
    sepBy propValParser (symb ",")

Had to do the same for jsonValParser.

jsonValParser = do
    _ <- pure 1
    jsonIntParser <|> jsonBoolParser <|> jsonStringParser <|> jsonObjParser

The idea is to defer the execution of the code which might lead to cyclic dependency. The added line, _ <- pure 1, does exactly that. I think it might be doing the same as fix from Data.Fix does or what defer from Data.Lazy does.

Mukesh Soni
  • 6,646
  • 3
  • 30
  • 37