DEFINITION MODULE Parser;

FROM Common IMPORT Identifier, PIdentifier, ShortString;
FROM Lexer IMPORT PLexer;

TYPE
  Parser = RECORD
    lexer: PLexer
  END;
  PParser = POINTER TO Parser;

  AstLiteralKind = (
    astLiteralKindInteger,
    astLiteralKindString,
    astLiteralKindNull,
    astLiteralKindBoolean
  );
  AstLiteral = RECORD
    CASE kind: AstLiteralKind OF
      astLiteralKindInteger: integer: INTEGER |
      astLiteralKindString: string: ShortString |
      astLiteralKindNull: |
      astLiteralKindBoolean: boolean: BOOLEAN
    END
  END;
  PAstLiteral = POINTER TO AstLiteral;

  AstUnaryOperator = (
    astUnaryOperatorReference,
    astUnaryOperatorNot,
    astUnaryOperatorMinus
  );
  AstBinaryOperator = (
    astBinaryOperatorSum,
    astBinaryOperatorSubtraction,
    astBinaryOperatorMultiplication,
    astBinaryOperatorDivision,
    astBinaryOperatorRemainder,
    astBinaryOperatorEquals,
    astBinaryOperatorNotEquals,
    astBinaryOperatorLess,
    astBinaryOperatorGreater,
    astBinaryOperatorLessEqual,
    astBinaryOperatorGreaterEqual,
    astBinaryOperatorDisjunction,
    astBinaryOperatorConjunction,
    astBinaryOperatorExclusiveDisjunction,
    astBinaryOperatorShiftLeft,
    astBinaryOperatorShiftRight
  );

  AstExpressionKind = (
    astExpressionKindLiteral,
    astExpressionKindIdentifier,
    astExpressionKindArrayAccess,
    astExpressionKindDereference,
    astExpressionKindFieldAccess,
    astExpressionKindUnary,
    astExpressionKindBinary,
    astExpressionKindCall
  );
  AstExpression = RECORD 
    CASE kind: AstExpressionKind OF
      astExpressionKindLiteral: literal: PAstLiteral |
      astExpressionKindIdentifier: identifier: Identifier |
      astExpressionKindDereference: reference: PAstExpression |
      astExpressionKindArrayAccess:
        array: PAstExpression;
        index: PAstExpression |
      astExpressionKindFieldAccess:
        aggregate: PAstExpression;
        field: Identifier |
      astExpressionKindUnary:
        unary_operator: AstUnaryOperator;
        unary_operand: PAstExpression |
      astExpressionKindBinary:
        binary_operator: AstBinaryOperator;
        lhs: PAstExpression;
        rhs: PAstExpression |
      astExpressionKindCall:
        callable: PAstExpression;
        argument_count: CARDINAL;
        arguments: PPAstExpression
    END
  END;
  PAstExpression = POINTER TO AstExpression;
  PPAstExpression = POINTER TO PAstExpression;

  AstStatementKind = (
    astStatementKindIf,
    astStatementKindWhile,
    astStatementKindAssignment,
    astStatementKindReturn,
    astStatementKindCall
  );
  AstStatement = RECORD
    CASE kind: AstStatementKind OF
      astStatementKindIf:
        if_condition: PAstExpression;
        if_branch: AstCompoundStatement |
      astStatementKindWhile:
        while_condition: PAstExpression;
        while_body: AstCompoundStatement |
      astStatementKindAssignment:
        assignee: PAstExpression;
        assignment: PAstExpression |
      astStatementKindReturn: returned: PAstExpression |
      astStatementKindCall: call: PAstExpression
    END
  END;
  PAstStatement = POINTER TO AstStatement;
  PPAstStatement = POINTER TO PAstStatement;
  AstCompoundStatement = RECORD
    count: CARDINAL;
    statements: PPAstStatement
  END;

  AstImportStatement = RECORD
    package: Identifier;
    symbols: PIdentifier
  END;
  PAstImportStatement = POINTER TO AstImportStatement;
  PPAstImportStatement = POINTER TO PAstImportStatement;

  AstConstantDeclaration = RECORD
    constant_name: Identifier;
    constant_value: INTEGER
  END;
  PAstConstantDeclaration = POINTER TO AstConstantDeclaration;
  PPAstConstantDeclaration = POINTER TO PAstConstantDeclaration;

  AstFieldDeclaration = RECORD
    field_name: Identifier;
    field_type: PAstTypeExpression
  END;
  PAstFieldDeclaration = POINTER TO AstFieldDeclaration;

  AstTypeExpressionKind = (
    astTypeExpressionKindNamed,
    astTypeExpressionKindRecord,
    astTypeExpressionKindEnumeration,
    astTypeExpressionKindArray,
    astTypeExpressionKindPointer,
    astTypeExpressionKindProcedure
  );
  AstTypeExpression = RECORD
    CASE kind: AstTypeExpressionKind OF
      astTypeExpressionKindNamed: name: Identifier |
      astTypeExpressionKindEnumeration: cases: PIdentifier |
      astTypeExpressionKindPointer: target: PAstTypeExpression |
      astTypeExpressionKindRecord: fields: PAstFieldDeclaration |
      astTypeExpressionKindArray:
        base: PAstTypeExpression;
        length: CARDINAL |
      astTypeExpressionKindProcedure: parameters: PPAstTypeExpression
    END
  END;
  PAstTypeExpression = POINTER TO AstTypeExpression;
  PPAstTypeExpression = POINTER TO PAstTypeExpression;

  AstTypedDeclaration = RECORD
    identifier: Identifier;
    type_expression: PAstTypeExpression
  END;
  PAstTypedDeclaration = POINTER TO AstTypedDeclaration;
  PPAstTypedDeclaration = POINTER TO PAstTypedDeclaration;

  AstVariableDeclaration = RECORD
    variable_name: Identifier;
    variable_type: PAstTypeExpression
  END;
  PAstVariableDeclaration = POINTER TO AstVariableDeclaration;
  PPAstVariableDeclaration = POINTER TO PAstVariableDeclaration;

  AstProcedureDeclaration = RECORD
    name: Identifier;
    parameter_count: CARDINAL;
    parameters: PAstTypedDeclaration;
    return_type: PAstTypeExpression;
    constants: PPAstConstantDeclaration;
    variables: PPAstVariableDeclaration;
    statements: AstCompoundStatement
  END;
  PAstProcedureDeclaration = POINTER TO AstProcedureDeclaration;
  PPAstProcedureDeclaration = POINTER TO PAstProcedureDeclaration;

  AstModule = RECORD
    main: BOOLEAN;
    imports: PPAstImportStatement;
    constants: PPAstConstantDeclaration;
    types: PPAstTypedDeclaration;
    variables: PPAstVariableDeclaration;
    procedures: PPAstProcedureDeclaration;
    statements: AstCompoundStatement
  END;
  PAstModule = POINTER TO AstModule;

PROCEDURE parse(lexer: PLexer): PAstModule;

END Parser.
