#**************************************************************************
#*                                                                        *
#*                                 OCaml                                  *
#*                                                                        *
#*            Gabriel Scherer, projet Parsifal, INRIA Saclay              *
#*                                                                        *
#*   Copyright 2018 Institut National de Recherche en Informatique et     *
#*     en Automatique.                                                    *
#*                                                                        *
#*   All rights reserved.  This file is distributed under the terms of    *
#*   the GNU Lesser General Public License version 2.1, with the          *
#*   special exception on linking described in the file LICENSE.          *
#*                                                                        *
#**************************************************************************

# The rules in this Makefile use Menhir to rebuild the OCaml compiler
# parser. They are included in the main Makefile, so should be invoked
# directly, for example 'make promote-menhir'. They must be called
# after any modification to parsing/parser.mly, for the modification
# to affect the parser linked in the produced compiler:
#
# - promote-menhir builds the parser from parser.mly and stores it in
#   the boot/ directory, so that future builds of the compiler use the
#   updated result. Use it to make permanent changes to the compiler
#   parser.
#
# - demote-menhir undoes the effect of promote-menhir. The files in
#   the boot/ directory that are affected by promote-menhir and are
#   under version control are restored to their normal state (HEAD).
#
# - test-menhir builds the parser from parser.mly without storing it
#   in the boot/ directory, and only checks that the generated parser
#   builds correctly. Use it to quickly check if a parser.mly change
#   breaks the build. If you want to test a compiler produced with
#   the new parser, you must use promote-menhir instead.
#   (Using this rule requires a partial compiler build as obtained
#    by 'make core' or 'make world'.)
#
# - clean-menhir removes the files generated by Menhir from parsing/,
#   keeping only the reference sources for the grammar.
#
# - depend-menhir updates the dependency information for the
#   Menhir-generated parser, which is versioned in the OCaml repository
#   like all other .depend files. It should be used when the dependencies
#   (of the OCaml code in the grammar semantic actions) change.

MENHIR ?= menhir

## Unused tokens

# tokens COMMENT, DOCSTRING and EOL are produced by special lexer
# modes used by other consumers than the parser.

# GREATERBRACKET ">]" was added by the parser by symmetry with "[<"
# (which is used in polymorphic variant), but is not currently used by
# the grammar.

unused_tokens := COMMENT DOCSTRING EOL GREATERRBRACKET

## Menhir compilation flags

MENHIRFLAGS := --explain --dump --ocamlc "$(CAMLC) $(COMPFLAGS)" --infer \
	--lalr --strict --table -lg 1 -la 1 \
        $(addprefix --unused-token ,$(unused_tokens)) --fixed-exception

## promote-menhir

.PHONY: promote-menhir
promote-menhir: parsing/parser.mly
	@ $(MAKE) import-menhirLib
	$(MENHIR) $(MENHIRFLAGS) parsing/parser.mly
# The generated parser.ml may contain lexer directives containing
# the absolute path to Menhir's standard library on the promoter's machine.
# This is benign but will generate pointless churn if another developer
# rebuilds the same grammar (from the same Menhir version).
	@ for f in $(addprefix parser.,ml mli) ; do \
	  sed \
	    's,^#\(.*\)"[^"]*/menhir/standard.mly",#\1"menhir/standard.mly",g' \
	    parsing/$$f \
	    > boot/menhir/$$f; \
	  rm parsing/$$f; \
	done

# The import-menhirLib invocation in promote-menhir ensures that each
# update of the boot/ parser is paired with an update of the imported
# menhirLib; otherwise it would be easy to generate a parser and keep
# an incompatible version of menhirLib, which would fail at
# compile-time.

.PHONY: import-menhirLib
import-menhirLib:
	@ mkdir -p boot/menhir
	@ cp \
           $(addprefix `$(MENHIR) --suggest-menhirLib`/menhirLib.,ml mli) \
           boot/menhir


## demote-menhir

DEMOTE:=menhirLib.ml menhirLib.mli parser.ml parser.mli

.PHONY: demote-menhir
demote-menhir:
	git checkout HEAD -- $(addprefix boot/menhir/,$(DEMOTE))

## test-menhir

# This rule assumes that the `parsing/` sources and its dependencies
# have already been compiled; 'make core' suffices to be in that
# state. We don't make 'core' an explicit dependency, as building
# 'test-menhir' repeatedly would rebuild the compiler each time
# (parser.ml has changed), without actually taking the changes from
# parser.mly into account ('core' uses the parser from boot/).

# The test-menhir target does not read or write the boot directory,
# it directly builds the parser in parsing/. In particular, it must
# duplicate the MenhirLib->CamlinternalMenhirlib renaming usually
# performed by the parsing/parser.ml import rule in the main
# Makefile.
.PHONY: test-menhir
test-menhir: parsing/parser.mly
	$(MENHIR) $(MENHIRFLAGS) parsing/parser.mly
	for f in $(addprefix parsing/parser.,ml mli) ; do \
	  cat $$f | sed "s/MenhirLib/CamlinternalMenhirLib/g" > $$f.tmp && \
	  mv $$f.tmp $$f ; \
	done
	$(MAKE) parsing/parser.cmo


## clean-menhir

partialclean-menhir::
	rm -f \
	  $(addprefix parsing/parser.,ml mli) \
	  $(addprefix parsing/camlinternalMenhirLib.,ml mli)

clean-menhir: partialclean-menhir


## depend-menhir

.PHONY: depend-menhir
depend-menhir:
	$(MENHIR) --depend --ocamldep "$(CAMLDEP) $(DEPFLAGS) $(DEPINCLUDES)" \
          parsing/parser.mly > .depend.menhir
# this rule depends on the variables CAMLDEP, DEPFLAGS, DEPINCLUDES
# defined in Makefile, so it can only be invoked from the main Makefile

include .depend.menhir

## interpret-menhir

# This rule runs Menhir in interactive mode.
# The user can enter sentences, such as:
#   implementation: TYPE LIDENT EQUAL LIDENT EOF
# and see how Menhir interprets them.

interpret-menhir:
	@ echo "Please wait, I am building the LALR automaton..."
	@ $(MENHIR) $(MENHIRFLAGS) parsing/parser.mly \
	    --interpret \
	    --interpret-show-cst \
	    --trace \
