//: Introduce a new transform to perform various checks in instructions before
//: we start running them. It'll be extensible, so that we can add checks for
//: new recipes as we extend 'run' to support them.
//:
//: Doing checking in a separate part complicates things, because the values
//: of variables in memory and the processor (current_recipe_name,
//: current_instruction) aren't available at checking time. If I had a more
//: sophisticated layer system I'd introduce the simpler version first and
//: transform it in a separate layer or set of layers.

:(before "End Checks")
Transform.push_back(check_instruction);  // idempotent

:(code)
void check_instruction(const recipe_ordinal r) {
  trace(9991, "transform") << "--- perform checks for recipe " << get(Recipe, r).name << end();
//?   cerr << "--- perform checks for recipe " << get(Recipe, r).name << '\n';
  map<string, vector<type_ordinal> > metadata;
  for (long long int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
    instruction& inst = get(Recipe, r).steps.at(i);
    if (inst.is_label) continue;
    switch (inst.operation) {
      // Primitive Recipe Checks
      case COPY: {
        if (SIZE(inst.products) != SIZE(inst.ingredients)) {
          raise_error << "ingredients and products should match in '" << inst.to_string() << "'\n" << end();
          break;
        }
        for (long long int i = 0; i < SIZE(inst.ingredients); ++i) {
          if (!types_coercible(inst.products.at(i), inst.ingredients.at(i))) {
            raise_error << maybe(get(Recipe, r).name) << "can't copy " << inst.ingredients.at(i).original_string << " to " << inst.products.at(i).original_string << "; types don't match\n" << end();
            goto finish_checking_instruction;
          }
        }
        break;
      }
      // End Primitive Recipe Checks
      default: {
        // Defined Recipe Checks
        // End Defined Recipe Checks
      }
    }
    finish_checking_instruction:;
  }
}

:(scenario copy_checks_reagent_count)
% Hide_errors = true;
recipe main [
  1:number <- copy 34, 35
]
+error: ingredients and products should match in '1:number <- copy 34, 35'

:(scenario write_scalar_to_array_disallowed)
% Hide_errors = true;
recipe main [
  1:array:number <- copy 34
]
+error: main: can't copy 34 to 1:array:number; types don't match

:(scenario write_scalar_to_array_disallowed_2)
% Hide_errors = true;
recipe main [
  1:number, 2:array:number <- copy 34, 35
]
+error: main: can't copy 35 to 2:array:number; types don't match

:(scenario write_scalar_to_address_disallowed)
% Hide_errors = true;
recipe main [
  1:address:number <- copy 34
]
+error: main: can't copy 34 to 1:address:number; types don't match

:(scenario write_address_to_number_allowed)
% Hide_errors = true;
recipe main [
  1:address:number <- copy 12/unsafe
  2:number <- copy 1:address:number
]
+mem: storing 12 in location 2
$error: 0

:(code)
// types_match with some leniency
bool types_coercible(const reagent& lhs, const reagent& rhs) {
  if (types_match(lhs, rhs)) return true;
  if (is_mu_address(rhs) && is_mu_number(lhs)) return true;
  // End types_coercible Special-cases
  return false;
}

bool types_match(const reagent& lhs, const reagent& rhs) {
  // to sidestep type-checking, use /unsafe in the source.
  // this will be highlighted in red inside vim. just for setting up some tests.
  if (is_unsafe(rhs)) return true;
  if (is_literal(rhs)) {
    if (is_mu_array(lhs)) return false;
    // End Matching Types For Literal(lhs)
    // allow writing 0 to any address
    if (is_mu_address(lhs)) return rhs.name == "0";
    if (!lhs.type) return false;
    if (lhs.type->value == get(Type_ordinal, "boolean"))
      return boolean_matches_literal(lhs, rhs);
    return size_of(lhs) == 1;  // literals are always scalars
  }
  return types_strictly_match(lhs, rhs);
}

bool boolean_matches_literal(const reagent& lhs, const reagent& rhs) {
  if (!is_literal(rhs)) return false;
  if (!lhs.type) return false;
  if (lhs.type->value != get(Type_ordinal, "boolean")) return false;
  return rhs.name == "0" || rhs.name == "1";
}

// copy arguments because later layers will want to make changes to them
// without perturbing the caller
bool types_strictly_match
#	Common Makefile for W3 Library Code
#	-----------------------------------
#
#	(c) CERN 1990, 1991 -- see Copyright.html for conditions
#
# This file should be invariant between systems.
#	DEPENDENCIES NOT COMPLETE @@
#
#	make		Compile and link the software (private version)
#	make install	Copy it into the system (implies make)
#	make update	Copy installed version into installed version
#	make uninstall	Unlink installed version from the system
#	make clean	Remove intermediate files
#	make cleanall	Remove intremediate files and products
#
# Macros required to be defined already for make:
#
# CC		The C compiler
# CFLAGS	Flags for $(CC) -- except the -I which are below
# LFLAGS	Flags for ld
# LYFLAGS	Flags for Lynx
#
# WWW		The WWW source tree directory
#
# Macros needed for make install:
#
# LIBDIR	Directory for installed library
#______________________________________________________________________

#  If this env var is set to something else Some makes will use that instead
SHELL = /bin/sh

#	.h files are distributed but originally are made from the
#	self-documenting hypertext files.

.SUFFIXES: .h .html .htm
.html.h:
#	- chmod +w $*.h
#	www -w90 -na -to text/x-c $*.html > $*.h
#	chmod -w $*.h

# If this is actually run in a subdirectory,
#
WWW = ../..
# WWW = ../..	For [cernlib] build in this directory

WC = $(WWW)/Library
CMN = $(WWW)/Library/Implementation/
VMS = $(CMN)vms
# Where shall we put the objects and built library?

LOB = $(WTMP)/Library/$(WWW_MACH)

# Only needed if HTWAIS.c is to be compiled. Put into your Makefile.include
#  uncomment these and fill in WAISINC for adding direct wais access
#  to Lynx.
#HTWAIS = $(LOB)/HTWAIS.o
#WAIS = YES
#WAISINC = -I../../../../freeWAIS-0.202/ir
#WAISCFLAGS = -DDIRECT_WAIS
#

# This path, if relative, is taken relative to the directory
# in which this makefile is, not the pwd.  This screws up the
# recursive invocation
# include $(CMN)Version.make
include $(ABS)$(WWW)/Library/Implementation/Version.make

# XMOsAIC hack is only for server to cope with xmosaic kludge for mmedia
#
# add -DNEW_GATEWAY here for the new gateway config stuff
CFLAGS2 = $(CFLAGS) $(LYFLAGS) $(WAISCFLAGS) -I$(CMN) -DXMOSAIC_HACK -DACCESS_AUTH

CERNLIBBIN = $(WWW)/bin

COMMON = $(LOB)/HTParse.o $(LOB)/HTAccess.o $(LOB)/HTTP.o \
	$(LOB)/HTFile.o	$(LOB)/HTBTree.o $(LOB)/HTFTP.o $(LOB)/HTTCP.o \
	$(LOB)/SGML.o $(LOB)/HTMLDTD.o $(LOB)/HTChunk.o \
	$(LOB)/HTPlain.o \
	$(LOB)/HTMLGen.o \
	$(LOB)/HTAtom.o $(LOB)/HTAnchor.o $(LOB)/HTStyle.o \
	$(LOB)/HTList.o $(LOB)/HTString.o $(LOB)/HTDOS.o \
	$(LOB)/HTRules.o $(LOB)/HTFormat.o  $(LOB)/HTMIME.o \
	$(LOB)/HTNews.o $(LOB)/HTGopher.o \
	$(LOB)/HTTelnet.o $(LOB)/HTFinger.o $(LOB)/HTWSRC.o $(HTWAIS) \
	$(LOB)/HTAAUtil.o $(LOB)/HTAABrow.o \
	$(LOB)/HTGroup.o  \
	$(LOB)/HTAAProt.o \
	$(LOB)/HTAssoc.o  $(LOB)/HTLex.o    $(LOB)/HTUU.o

CFILES = $(CMN)HTParse.c $(CMN)HTAccess.c $(CMN)HTTP.c $(CMN)HTFile.c \
	$(CMN)HTBTree.c \
	$(CMN)HTFTP.c	 $(CMN)HTTCP.c	   $(CMN)SGML.c \
	$(CMN)HTMLDTD.c \
	$(CMN)HTPlain.c	 \
	$(CMN)HTDOS.c	 $(CMN)HTMLGen.c \
	$(CMN)HTChunk.c	 $(CMN)HTAtom.c	  $(CMN)HTAnchor.c $(CMN)HTStyle.c \
	$(CMN)HTList.c	 $(CMN)HTString.c $(CMN)HTRules.c \
	$(CMN)HTFormat.c $(CMN)HTMIME.c \
	$(CMN)HTNews.c	 $(CMN)HTGopher.c $(CMN)HTTelnet.c \
	$(CMN)HTFinger.c $(CMN)HTWAIS.c	  $(CMN)HTWSRC.c \
	$(CMN)HTAAUtil.c $(CMN)HTAABrow.c \
	$(CMN)HTGroup.c  \
	$(CMN)HTAAProt.c \
	$(CMN)HTAssoc.c  $(CMN)HTLex.c    $(CMN)HTUU.c

HFILES = $(CMN)HTParse.h $(CMN)HTAccess.h $(CMN)HTTP.h $(CMN)HTFile.h \
	$(CMN)HTBTree.h $(CMN)HTFTP.h $(CMN)HTTCP.h \
	$(CMN)SGML.h $(CMN)HTML.h $(CMN)HTMLDTD.h $(CMN)HTChunk.h \
	$(CMN)HTPlain.h	\
	$(CMN)HTFWriter.h	$(CMN)HTMLGen.h $(CMN)HTDOS.h \
	$(CMN)HTStream.h \
	$(CMN)HTAtom.h $(CMN)HTAnchor.h $(CMN)HTStyle.h \
	$(CMN)HTList.h \
	$(CMN)HTString.h $(CMN)HTRules.h \