haskellhappy

Haskell Happy parser error mismatching types and infinite type


Writing a Oberon-like language parser, I'm having troubles compiling the parser after I've updated it to be able to define more procedures on the same level, and not only nested one inside the other.

This is my lexer:

{
module Lexer where
}

%wrapper "basic"

$alpha      = [a-zA-Z]
$digit      = [0-9]
$validChar  = [^\"]

tokens :-

  $white+                             ;
  "PROCEDURE"                 { \s -> KW_TokenProcedure }
  "END"                       { \s -> KW_TokenEnd }
  ";"                         { \s -> KW_TokenSemiColon }
  $alpha [$alpha $digit \_]*  { \s -> TokenVariableIdentifier s }

{

-- The token type:
data Token =
  KW_TokenProcedure               |
  KW_TokenEnd                     |
  KW_TokenSemiColon               |
  TokenVariableIdentifier String  |
    deriving (Eq,Show)
}

This is my parser:

{
module Main where
import Lexer
import Tools
}

%name myParse
%tokentype { Token }
%error { parseError }

%token
  KW_PROCEDURE          { KW_TokenProcedure }
  KW_END                { KW_TokenEnd }
  ';'                   { KW_TokenSemiColon }
  identifier            { TokenVariableIdentifier $$ }

%%

ProcedureDeclarationList  :   ProcedureDeclaration                              { $1 }
                          |   ProcedureDeclaration ';' ProcedureDeclarationList { $3 : $1 }

ProcedureDeclaration  : ProcedureHeading ';' ProcedureBody identifier   {
                                                                          do
                                                                            let newProc = $1  -- Crea la nuova procedura
                                                                            let procBody = $3
                                                                            addProcedureToProcedure newProc procBody
                                                                        }

ProcedureHeading        :   KW_PROCEDURE identifier { defaultProcedure { procedureName = $2 } }

ProcedureBody           : KW_END                                    { Nothing }
                        | DeclarationSequence KW_END                { Just $1 }

DeclarationSequence     :    ProcedureDeclarationList                 { $1 }

{
parseError :: [Token] -> a
parseError _ = error "Parse error"

main = do
  inStr <- getContents
  let result = oLikeParse (alexScanTokens inStr)
  putStrLn ("result: " ++ show(result))
}

And this is the module where the types and some utility functions are defined:

module Tools where

data Procedure = Procedure {    procedureName :: String,
                                procedureProcedures :: [Procedure] } deriving (Show)

defaultProcedure = Procedure {  procedureName = "",
                                procedureProcedures = [] }

addProcedureToProcedure :: Procedure -> Maybe Procedure -> Procedure
addProcedureToProcedure procDest Nothing            = Procedure {   procedureName = (procedureName procDest),
                                                                    procedureProcedures = (procedureProcedures procDest) }
addProcedureToProcedure procDest (Just procToAdd)   = Procedure {   procedureName = (procedureName procDest),
                                                                    procedureProcedures = (procedureProcedures procDest) ++ [procToAdd] }

The errors the compiler is giving me are these two:

I've isolated the problem and I know for sure that if I remove the second case of my ProcedureDeclarationList everything compiles fine, but I can't recognize more procedures on the same level.


UPDATE

I've changed the my data structure so that I'm not using Maybe Procedure anymore and I won't need a list of two types, but I still have a problem with mismatching types.

This is my updated parser:

{
module Main where
import Lexer
import Tools
}

%name myParse
%tokentype { Token }
%error { parseError }

%token
  KW_INTEGER            { KW_TokenInteger }
  KW_REAL               { KW_TokenReal }
  KW_BOOLEAN            { KW_TokenBoolean }
  KW_CHAR               { KW_TokenChar }
  KW_PROCEDURE          { KW_TokenProcedure }
  KW_END                { KW_TokenEnd }
  KW_VAR                { KW_TokenVar }
  ';'                   { KW_TokenSemiColon }
  ','                   { KW_TokenComa }
  ':'                   { KW_TokenColon }
  identifier            { TokenVariableIdentifier $$ }

%%

ProcedureDeclarationList  :   ProcedureDeclaration                              { [$1] }
                          |   ProcedureDeclaration ';' ProcedureDeclarationList { $1:$3 }

ProcedureDeclaration  : ProcedureHeading ';' ProcedureBody identifier { defaultDeclaration { declarationType = DT_Procedure, procedureDeclared = (addBodyToProcedure $1 $3)} }

IdentifiersList     :   identifier                      { [$1] }
                    |   identifier ',' IdentifiersList  { $1:$3 }

VariableDeclaration : IdentifiersList ':' type          { createVariablesDefinitionsOfType $1 $3 }

ProcedureHeading    : KW_PROCEDURE identifier { defaultProcedure { procedureName = $2 } }

ProcedureBody     : KW_END                                      { [] }
                  | DeclarationSequence KW_END                  { $1 }

DeclarationSequence   : KW_VAR VariableDeclarationList ';'      { $2 }
                      | ProcedureDeclarationList                { $1 }

VariableDeclarationList : VariableDeclaration                             { [$1] }
                        | VariableDeclaration ';' VariableDeclarationList { $1:$3 }

type        :   KW_INTEGER    { Integer }
            |   KW_REAL       { Float }
            |   KW_BOOLEAN    { Boolean }
            |   KW_CHAR       { Char }

{
parseError :: [Token] -> a
parseError _ = error "Parse error"

main = do
  inStr <- getContents
  let result = oLikeParse (alexScanTokens inStr)
  putStrLn ("result: " ++ show(result))
}

This is my updated lexer:

{
module Lexer where
}

%wrapper "basic"

$alpha      = [a-zA-Z]
$digit      = [0-9]
$validChar  = [^\"]

tokens :-

  $white+                             ;
  "INTEGER"                   { \s -> KW_TokenInteger }
  "REAL"                      { \s -> KW_TokenReal }
  "BOOLEAN"                   { \s -> KW_TokenBoolean }
  "CHAR"                      { \s -> KW_TokenChar }
  "PROCEDURE"                 { \s -> KW_TokenProcedure }
  "END"                       { \s -> KW_TokenEnd }
  "VAR"                       { \s -> KW_TokenVar }
  ";"                         { \s -> KW_TokenSemiColon }
  ","                         { \s -> KW_TokenComa }
  ":"                         { \s -> KW_TokenColon }
  $alpha [$alpha $digit \_]*  { \s -> TokenVariableIdentifier s }

{

-- The token type:
data Token =
  KW_TokenInteger                 |
  KW_TokenReal                    |
  KW_TokenBoolean                 |
  KW_TokenChar                    |
  KW_TokenVar                     |
  KW_TokenProcedure               |
  KW_TokenEnd                     |
  KW_TokenSemiColon               |
  KW_TokenComa                    |
  KW_TokenColon                   |
  TokenVariableIdentifier String  |
    deriving (Eq,Show)
}

And this is my updated tools module:

module Tools where

data AttributeType  = String
                    | Float
                    | Char
                    | Integer
                    | Boolean
                    deriving (Show, Eq)

data Attribute = Attribute {    attributeName :: String,
                                attributeType :: AttributeType,
                                stringValue :: String,
                                floatValue :: Float,
                                integerValue :: Integer,
                                charValue :: Char,
                                booleanValue :: Bool } deriving (Show)

data Procedure = Procedure {    procedureName :: String,
                                attributes :: [Attribute],
                                procedureProcedures :: [Procedure] } deriving (Show)

data DeclarationType    = DT_Variable
                        | DT_Constant
                        | DT_Procedure
                        deriving (Show, Eq)

data Declaration = Declaration {    declarationType     :: DeclarationType,
                                    attributeDeclared   :: Attribute,
                                    procedureDeclared   :: Procedure } deriving (Show)

defaultAttribute = Attribute {  attributeName = "",
                                attributeType = Integer,
                                stringValue = "",
                                floatValue = 0.0,
                                integerValue = 0,
                                charValue = ' ',
                                booleanValue = False }

defaultProcedure = Procedure {  procedureName = "",
                                attributes = [],
                                procedureProcedures = [] }

defaultDeclaration = Declaration {  declarationType = DT_Variable,
                                    attributeDeclared = defaultAttribute,
                                    procedureDeclared = defaultProcedure }

addAttributeToProcedure :: Procedure -> Attribute -> Procedure
addAttributeToProcedure proc att = Procedure {  procedureName = (procedureName proc),
                                                attributes = (attributes proc) ++ [att],
                                                procedureProcedures = (procedureProcedures proc) }

addProcedureToProcedure :: Procedure -> Procedure -> Procedure
addProcedureToProcedure procDest procToAdd  = Procedure {   procedureName = (procedureName procDest),
                                                            attributes = (attributes procDest),
                                                            procedureProcedures = (procedureProcedures procDest) ++ [procToAdd] }

addBodyToProcedure :: Procedure -> [Declaration] -> Procedure
addBodyToProcedure procDest []          =   procDest
addBodyToProcedure procDest declList    = do 
                                            let decl = head declList
                                            let declType = declarationType decl

                                            if declType == DT_Variable || declType == DT_Constant then 
                                                addBodyToProcedure (addAttributeToProcedure procDest (attributeDeclared decl)) (tail declList)
                                            else
                                                addBodyToProcedure (addProcedureToProcedure procDest (procedureDeclared decl)) (tail declList)

createVariablesDefinitionsOfType :: [String] -> AttributeType -> [Declaration]
createVariablesDefinitionsOfType namesList t = map (\x -> defaultDeclaration { declarationType = DT_Variable, attributeDeclared = (defaultAttribute {attributeName = x, attributeType = t})} ) namesList

This is the schema of the production types:

PRODUCTION                  TYPE
---------------             ---------------
ProcedureDeclarationList    [Declaration]
ProcedureDeclaration        Declaration
IdentifiersList             [String]
VariableDeclaration         [Declaration]
ProcedureHeading            Procedure
ProcedureBody               [Declaration]
DeclarationSequence         [Declaration]
VariableDeclarationList     [Declaration]
type                        AttributeType

This are the only 3 errors I get now:


Solution

  • Keeping track of the types of each production with comments will help you track down type errors.

    Here the types each production should have:

    Production                            Type
    --------------                        ---------
    ProcedureHeading                      Procedure
    ProcedureDeclaration                  Procedure
    ProcedureDeclarationList              [ Procedure ]
    DeclarationSequence                   [ Procedure ]
    ProcedureBody                         Maybe Procedure
    

    Now check each of your production rules to see if they have the correct type.

    1. ProcedureHeading returns defaultProcedure with a changed name, so that's ok.

    2. ProcedureDeclaration returns the result of a call to addProcedureToProcedure, so that checks out. Note that the second argument to the addProcedureToProcedure call is $3 which refers to the result of a ProcedureBody production, so that means the return type of that production must be a Maybe Procedure.

    3. ProcedureDeclarationList has problems. The production rules should read:

      ProcedureDeclarationList
        : ProcedureDeclaration                              { [ $1 ] }
        | ProcedureDeclaration ';' ProcedureDeclarationList { $1 : $3 }
      

    [$1] makes a list out of a single Procedure, and $1:$3 prepends a single Procedure to a list of Procedures.

    1. DeclarationSequence is simply a ProcedureDeclarationList, so that checks out.

    2. As noted in step 2, ProcedureBody has to be a Maybe Procedure. The rule for KW_END is fine, but the second rule needs some work:

      ProcedureBody
          | KW_END                     { Nothing }
          | DeclarationSequence KW_END { ??? }
      

    From a DeclarationSequence (which is a [Procedure]) we have to produce a Maybe Procedure. This is where your problem is. Just $1 has type Maybe [Procedure], so that isn't going to work here.