Interpretation of a Nit program directly on the AST

Introduced classes

class CallrefInstance

nitc :: CallrefInstance

An instance with the original receiver and callsite (for function reference)
class FatalError

nitc :: FatalError

A runtime error
abstract class Frame

nitc :: Frame

Information about local variables in a running method
abstract class Instance

nitc :: Instance

An instance represents a value of the executed program.
class InterpreterFrame

nitc :: InterpreterFrame

Implementation of a Frame with a Hashmap to store local variables
class MutableInstance

nitc :: MutableInstance

A instance with attribute (standards objects)
class NaiveInterpreter

nitc :: NaiveInterpreter

The visitor that interprets the Nit Program by walking on the AST
class PrimitiveInstance[E: nullable Object]

nitc :: PrimitiveInstance

Special instance to handle primitives values (int, bool, etc.)

Redefined classes

redef class AAbortExpr

nitc :: naive_interpreter $ AAbortExpr

An abort statement
redef class AAndExpr

nitc :: naive_interpreter $ AAndExpr

A and expression
redef class AArrayExpr

nitc :: naive_interpreter $ AArrayExpr

A literal array. eg. [x,y,z]
redef class AAsCastExpr

nitc :: naive_interpreter $ AAsCastExpr

A type cast. eg x.as(T)
redef class AAsNotnullExpr

nitc :: naive_interpreter $ AAsNotnullExpr

A as-not-null cast. eg x.as(not null)
redef class AAssertExpr

nitc :: naive_interpreter $ AAssertExpr

An assert statement
redef class AAttrAssignExpr

nitc :: naive_interpreter $ AAttrAssignExpr

The assignment of an attribute. eg x._a=y
redef class AAttrExpr

nitc :: naive_interpreter $ AAttrExpr

The read of an attribute. eg x._a
redef class AAttrPropdef

nitc :: naive_interpreter $ AAttrPropdef

A definition of an attribute
redef class AAttrReassignExpr

nitc :: naive_interpreter $ AAttrReassignExpr

A complex attribute assignment. eg x._a+=y
redef class ABlockExpr

nitc :: naive_interpreter $ ABlockExpr

A sequence of AExpr (usually statements)
redef class ACallrefExpr

nitc :: naive_interpreter $ ACallrefExpr

A reference to a method with a captured receiver. eg. &x.foo or just &foo is self is captured.
redef class ACharExpr

nitc :: naive_interpreter $ ACharExpr

A character literal
redef abstract class AClassdef

nitc :: naive_interpreter $ AClassdef

A class definition
redef class ACrangeExpr

nitc :: naive_interpreter $ ACrangeExpr

A closed literal range. eg [x..y]
redef class ADebugTypeExpr

nitc :: naive_interpreter $ ADebugTypeExpr

A special expression to debug types
redef class ADoExpr

nitc :: naive_interpreter $ ADoExpr

A do statement
redef abstract class AEscapeExpr

nitc :: naive_interpreter $ AEscapeExpr

A break or a continue
redef abstract class AExpr

nitc :: naive_interpreter $ AExpr

Expression and statements
redef class AFalseExpr

nitc :: naive_interpreter $ AFalseExpr

A false boolean literal constant
redef class AFloatExpr

nitc :: naive_interpreter $ AFloatExpr

A float literal
redef class AForExpr

nitc :: naive_interpreter $ AForExpr

A for statement
redef class AIfExpr

nitc :: naive_interpreter $ AIfExpr

A if statement
redef class AIfexprExpr

nitc :: naive_interpreter $ AIfexprExpr

A if expression (ternary conditional). eg. if true then 1 else 0
redef class AImplicitSelfExpr

nitc :: naive_interpreter $ AImplicitSelfExpr

When there is no explicit receiver, self is implicit
redef class AImpliesExpr

nitc :: naive_interpreter $ AImpliesExpr

A implies expression
redef class AIntegerExpr

nitc :: naive_interpreter $ AIntegerExpr

An integer literal
redef class AIsaExpr

nitc :: naive_interpreter $ AIsaExpr

A type-ckeck expression. eg x isa T
redef class AIssetAttrExpr

nitc :: naive_interpreter $ AIssetAttrExpr

A is-set check of old-style attributes. eg isset x._a
redef class ALoopExpr

nitc :: naive_interpreter $ ALoopExpr

A loop statement
redef class AMethPropdef

nitc :: naive_interpreter $ AMethPropdef

A definition of all kind of method (including constructors)
redef class ANamedargExpr

nitc :: naive_interpreter $ ANamedargExpr

An named notation used to pass an expression by name in a parameter
redef class ANewExpr

nitc :: naive_interpreter $ ANewExpr

An explicit instantiation. eg new T
redef abstract class ANode

nitc :: naive_interpreter $ ANode

Root of the AST class-hierarchy
redef class ANotExpr

nitc :: naive_interpreter $ ANotExpr

A not expression
redef class ANullExpr

nitc :: naive_interpreter $ ANullExpr

A null literal constant
redef class AOnceExpr

nitc :: naive_interpreter $ AOnceExpr

A once expression. eg once x
redef class AOrElseExpr

nitc :: naive_interpreter $ AOrElseExpr

A or else expression
redef class AOrExpr

nitc :: naive_interpreter $ AOrExpr

A or expression
redef class AOrangeExpr

nitc :: naive_interpreter $ AOrangeExpr

An open literal range. eg [x..y[
redef class AParExpr

nitc :: naive_interpreter $ AParExpr

A simple parenthesis. eg (x)
redef abstract class APropdef

nitc :: naive_interpreter $ APropdef

The definition of a property
redef class ASafeExpr

nitc :: naive_interpreter $ ASafeExpr

A receiver with a ? suffix used in safe call operator.
redef class ASelfExpr

nitc :: naive_interpreter $ ASelfExpr

A read of self
redef abstract class ASendExpr

nitc :: naive_interpreter $ ASendExpr

A polymorphic invocation of a method
redef abstract class ASendReassignFormExpr

nitc :: naive_interpreter $ ASendReassignFormExpr

A complex setter call (standard or brackets)
redef class AStringExpr

nitc :: naive_interpreter $ AStringExpr

A simple string. eg. "abc"
redef abstract class AStringFormExpr

nitc :: naive_interpreter $ AStringFormExpr

A string literal
redef class ASuperExpr

nitc :: naive_interpreter $ ASuperExpr

A call to super. OR a call of a super-constructor
redef class ASuperstringExpr

nitc :: naive_interpreter $ ASuperstringExpr

A superstring literal. eg "a{x}b{y}c"
redef class ATrueExpr

nitc :: naive_interpreter $ ATrueExpr

A true boolean literal constant
redef class AVarAssignExpr

nitc :: naive_interpreter $ AVarAssignExpr

A local variable simple assignment access
redef class AVarExpr

nitc :: naive_interpreter $ AVarExpr

A local variable read access.
redef class AVarReassignExpr

nitc :: naive_interpreter $ AVarReassignExpr

A local variable complex assignment access
redef class AVarargExpr

nitc :: naive_interpreter $ AVarargExpr

An ellipsis notation used to pass an expression as it, in a vararg parameter
redef class AVardeclExpr

nitc :: naive_interpreter $ AVardeclExpr

A declaration of a local variable. eg var x: X = y
redef class AWhileExpr

nitc :: naive_interpreter $ AWhileExpr

A while statement
redef class AWithExpr

nitc :: naive_interpreter $ AWithExpr

A with statement
redef class AugmentedStringFormExpr

nitc :: naive_interpreter $ AugmentedStringFormExpr

Any kind of string form with augmentations from prefixes or suffixes
redef class ModelBuilder

nitc :: naive_interpreter $ ModelBuilder

A model builder knows how to load nit source files and build the associated model
redef class ToolContext

nitc :: naive_interpreter $ ToolContext

Global context for tools

All class definitions

redef class AAbortExpr

nitc :: naive_interpreter $ AAbortExpr

An abort statement
redef class AAndExpr

nitc :: naive_interpreter $ AAndExpr

A and expression
redef class AArrayExpr

nitc :: naive_interpreter $ AArrayExpr

A literal array. eg. [x,y,z]
redef class AAsCastExpr

nitc :: naive_interpreter $ AAsCastExpr

A type cast. eg x.as(T)
redef class AAsNotnullExpr

nitc :: naive_interpreter $ AAsNotnullExpr

A as-not-null cast. eg x.as(not null)
redef class AAssertExpr

nitc :: naive_interpreter $ AAssertExpr

An assert statement
redef class AAttrAssignExpr

nitc :: naive_interpreter $ AAttrAssignExpr

The assignment of an attribute. eg x._a=y
redef class AAttrExpr

nitc :: naive_interpreter $ AAttrExpr

The read of an attribute. eg x._a
redef class AAttrPropdef

nitc :: naive_interpreter $ AAttrPropdef

A definition of an attribute
redef class AAttrReassignExpr

nitc :: naive_interpreter $ AAttrReassignExpr

A complex attribute assignment. eg x._a+=y
redef class ABlockExpr

nitc :: naive_interpreter $ ABlockExpr

A sequence of AExpr (usually statements)
redef class ACallrefExpr

nitc :: naive_interpreter $ ACallrefExpr

A reference to a method with a captured receiver. eg. &x.foo or just &foo is self is captured.
redef class ACharExpr

nitc :: naive_interpreter $ ACharExpr

A character literal
redef abstract class AClassdef

nitc :: naive_interpreter $ AClassdef

A class definition
redef class ACrangeExpr

nitc :: naive_interpreter $ ACrangeExpr

A closed literal range. eg [x..y]
redef class ADebugTypeExpr

nitc :: naive_interpreter $ ADebugTypeExpr

A special expression to debug types
redef class ADoExpr

nitc :: naive_interpreter $ ADoExpr

A do statement
redef abstract class AEscapeExpr

nitc :: naive_interpreter $ AEscapeExpr

A break or a continue
redef abstract class AExpr

nitc :: naive_interpreter $ AExpr

Expression and statements
redef class AFalseExpr

nitc :: naive_interpreter $ AFalseExpr

A false boolean literal constant
redef class AFloatExpr

nitc :: naive_interpreter $ AFloatExpr

A float literal
redef class AForExpr

nitc :: naive_interpreter $ AForExpr

A for statement
redef class AIfExpr

nitc :: naive_interpreter $ AIfExpr

A if statement
redef class AIfexprExpr

nitc :: naive_interpreter $ AIfexprExpr

A if expression (ternary conditional). eg. if true then 1 else 0
redef class AImplicitSelfExpr

nitc :: naive_interpreter $ AImplicitSelfExpr

When there is no explicit receiver, self is implicit
redef class AImpliesExpr

nitc :: naive_interpreter $ AImpliesExpr

A implies expression
redef class AIntegerExpr

nitc :: naive_interpreter $ AIntegerExpr

An integer literal
redef class AIsaExpr

nitc :: naive_interpreter $ AIsaExpr

A type-ckeck expression. eg x isa T
redef class AIssetAttrExpr

nitc :: naive_interpreter $ AIssetAttrExpr

A is-set check of old-style attributes. eg isset x._a
redef class ALoopExpr

nitc :: naive_interpreter $ ALoopExpr

A loop statement
redef class AMethPropdef

nitc :: naive_interpreter $ AMethPropdef

A definition of all kind of method (including constructors)
redef class ANamedargExpr

nitc :: naive_interpreter $ ANamedargExpr

An named notation used to pass an expression by name in a parameter
redef class ANewExpr

nitc :: naive_interpreter $ ANewExpr

An explicit instantiation. eg new T
redef abstract class ANode

nitc :: naive_interpreter $ ANode

Root of the AST class-hierarchy
redef class ANotExpr

nitc :: naive_interpreter $ ANotExpr

A not expression
redef class ANullExpr

nitc :: naive_interpreter $ ANullExpr

A null literal constant
redef class AOnceExpr

nitc :: naive_interpreter $ AOnceExpr

A once expression. eg once x
redef class AOrElseExpr

nitc :: naive_interpreter $ AOrElseExpr

A or else expression
redef class AOrExpr

nitc :: naive_interpreter $ AOrExpr

A or expression
redef class AOrangeExpr

nitc :: naive_interpreter $ AOrangeExpr

An open literal range. eg [x..y[
redef class AParExpr

nitc :: naive_interpreter $ AParExpr

A simple parenthesis. eg (x)
redef abstract class APropdef

nitc :: naive_interpreter $ APropdef

The definition of a property
redef class ASafeExpr

nitc :: naive_interpreter $ ASafeExpr

A receiver with a ? suffix used in safe call operator.
redef class ASelfExpr

nitc :: naive_interpreter $ ASelfExpr

A read of self
redef abstract class ASendExpr

nitc :: naive_interpreter $ ASendExpr

A polymorphic invocation of a method
redef abstract class ASendReassignFormExpr

nitc :: naive_interpreter $ ASendReassignFormExpr

A complex setter call (standard or brackets)
redef class AStringExpr

nitc :: naive_interpreter $ AStringExpr

A simple string. eg. "abc"
redef abstract class AStringFormExpr

nitc :: naive_interpreter $ AStringFormExpr

A string literal
redef class ASuperExpr

nitc :: naive_interpreter $ ASuperExpr

A call to super. OR a call of a super-constructor
redef class ASuperstringExpr

nitc :: naive_interpreter $ ASuperstringExpr

A superstring literal. eg "a{x}b{y}c"
redef class ATrueExpr

nitc :: naive_interpreter $ ATrueExpr

A true boolean literal constant
redef class AVarAssignExpr

nitc :: naive_interpreter $ AVarAssignExpr

A local variable simple assignment access
redef class AVarExpr

nitc :: naive_interpreter $ AVarExpr

A local variable read access.
redef class AVarReassignExpr

nitc :: naive_interpreter $ AVarReassignExpr

A local variable complex assignment access
redef class AVarargExpr

nitc :: naive_interpreter $ AVarargExpr

An ellipsis notation used to pass an expression as it, in a vararg parameter
redef class AVardeclExpr

nitc :: naive_interpreter $ AVardeclExpr

A declaration of a local variable. eg var x: X = y
redef class AWhileExpr

nitc :: naive_interpreter $ AWhileExpr

A while statement
redef class AWithExpr

nitc :: naive_interpreter $ AWithExpr

A with statement
redef class AugmentedStringFormExpr

nitc :: naive_interpreter $ AugmentedStringFormExpr

Any kind of string form with augmentations from prefixes or suffixes
class CallrefInstance

nitc $ CallrefInstance

An instance with the original receiver and callsite (for function reference)
class FatalError

nitc $ FatalError

A runtime error
abstract class Frame

nitc $ Frame

Information about local variables in a running method
abstract class Instance

nitc $ Instance

An instance represents a value of the executed program.
class InterpreterFrame

nitc $ InterpreterFrame

Implementation of a Frame with a Hashmap to store local variables
redef class ModelBuilder

nitc :: naive_interpreter $ ModelBuilder

A model builder knows how to load nit source files and build the associated model
class MutableInstance

nitc $ MutableInstance

A instance with attribute (standards objects)
class NaiveInterpreter

nitc $ NaiveInterpreter

The visitor that interprets the Nit Program by walking on the AST
class PrimitiveInstance[E: nullable Object]

nitc $ PrimitiveInstance

Special instance to handle primitives values (int, bool, etc.)
redef class ToolContext

nitc :: naive_interpreter $ ToolContext

Global context for tools
package_diagram nitc::naive_interpreter naive_interpreter nitc\>semantize\> semantize nitc::naive_interpreter->nitc\>semantize\> nitc::mixin mixin nitc::naive_interpreter->nitc::mixin nitc::serialize_model serialize_model nitc::naive_interpreter->nitc::serialize_model nitc::explain_assert_api explain_assert_api nitc::naive_interpreter->nitc::explain_assert_api nitc nitc nitc\>semantize\>->nitc nitc\>modelize\> modelize nitc\>semantize\>->nitc\>modelize\> nitc::modelbuilder modelbuilder nitc::mixin->nitc::modelbuilder json::serialization_write serialization_write nitc::serialize_model->json::serialization_write nitc\>model\> model nitc::serialize_model->nitc\>model\> nitc\>parser\> parser nitc::explain_assert_api->nitc\>parser\> ...nitc ... ...nitc->nitc ...nitc\>modelize\> ... ...nitc\>modelize\>->nitc\>modelize\> ...nitc::modelbuilder ... ...nitc::modelbuilder->nitc::modelbuilder ...json::serialization_write ... ...json::serialization_write->json::serialization_write ...nitc\>model\> ... ...nitc\>model\>->nitc\>model\> ...nitc\>parser\> ... ...nitc\>parser\>->nitc\>parser\> nitc::on_demand_compiler on_demand_compiler nitc::on_demand_compiler->nitc::naive_interpreter nitc::virtual_machine virtual_machine nitc::virtual_machine->nitc::naive_interpreter nitc::dynamic_loading_ffi dynamic_loading_ffi nitc::dynamic_loading_ffi->nitc::on_demand_compiler nitc::dynamic_loading_ffi... ... nitc::dynamic_loading_ffi...->nitc::dynamic_loading_ffi nitc::vm_optimizations vm_optimizations nitc::vm_optimizations->nitc::virtual_machine nitc::variables_numbering variables_numbering nitc::variables_numbering->nitc::virtual_machine nitc::vm_optimizations... ... nitc::vm_optimizations...->nitc::vm_optimizations nitc::variables_numbering... ... nitc::variables_numbering...->nitc::variables_numbering

Ancestors

module abstract_collection

core :: abstract_collection

Abstract collection classes and services.
module abstract_text

core :: abstract_text

Abstract class for manipulation of sequences of characters
module annotation

nitc :: annotation

Management and utilities on annotations
module array

core :: array

This module introduces the standard array structure.
module auto_super_init

nitc :: auto_super_init

Computing of super-constructors that must be implicitly called at the begin of constructors.
module bitset

core :: bitset

Services to handle BitSet
module bytes

core :: bytes

Services for byte streams and arrays
module caching

serialization :: caching

Services for caching serialization engines
module circular_array

core :: circular_array

Efficient data structure to access both end of the sequence.
module codec_base

core :: codec_base

Base for codecs to use with streams
module codecs

core :: codecs

Group module for all codec-related manipulations
module collection

core :: collection

This module define several collection classes.
module console

console :: console

Defines some ANSI Terminal Control Escape Sequences.
module core

core :: core

Standard classes and methods used by default by Nit programs and libraries.
module digraph

graph :: digraph

Implementation of directed graphs, also called digraphs.
module engine_tools

serialization :: engine_tools

Advanced services for serialization engines
module environ

core :: environ

Access to the environment variables of the process
module error

core :: error

Standard error-management infrastructure.
module exec

core :: exec

Invocation and management of operating system sub-processes.
module file

core :: file

File manipulations (create, read, write, etc.)
module fixed_ints

core :: fixed_ints

Basic integers of fixed-precision
module fixed_ints_text

core :: fixed_ints_text

Text services to complement fixed_ints
module flat

core :: flat

All the array-based text representations
module flow

nitc :: flow

Intraprocedural static flow.
module gc

core :: gc

Access to the Nit internal garbage collection mechanism
module hash_collection

core :: hash_collection

Introduce HashMap and HashSet.
module ini

ini :: ini

Read and write INI configuration files
module inspect

serialization :: inspect

Refine Serializable::inspect to show more useful information
module iso8859_1

core :: iso8859_1

Codec for ISO8859-1 I/O
module kernel

core :: kernel

Most basic classes and methods.
module lexer

nitc :: lexer

Lexer and its tokens.
module lexer_work

nitc :: lexer_work

Internal algorithm and data structures for the Nit lexer
module list

core :: list

This module handle double linked lists
module literal

nitc :: literal

Parsing of literal values in the abstract syntax tree.
module loader

nitc :: loader

Loading of Nit source files
module local_var_init

nitc :: local_var_init

Verify that local variables are initialized before their usage
module location

nitc :: location

Nit source-file and locations in source-file
module math

core :: math

Mathematical operations
module mdoc

nitc :: mdoc

Documentation of model entities
module meta

meta :: meta

Simple user-defined meta-level to manipulate types of instances as object.
module mmodule

nitc :: mmodule

modules and module hierarchies in the metamodel
module mmodule_data

nitc :: mmodule_data

Define and retrieve data in modules
module model

nitc :: model

Classes, types and properties
module model_base

nitc :: model_base

The abstract concept of model and related common things
module modelbuilder_base

nitc :: modelbuilder_base

Load nit source files and build the associated model
module modelize

nitc :: modelize

Create a model from nit source files
module modelize_class

nitc :: modelize_class

Analysis and verification of class definitions to instantiate model element
module modelize_property

nitc :: modelize_property

Analysis and verification of property definitions to instantiate model element
module more_collections

more_collections :: more_collections

Highly specific, but useful, collections-related classes.
module mpackage

nitc :: mpackage

Modelisation of a Nit package
module native

core :: native

Native structures for text and bytes
module nitpm_shared

nitc :: nitpm_shared

Services related to the Nit package manager
module numeric

core :: numeric

Advanced services for Numeric types
module opts

opts :: opts

Management of options on the command line
module ordered_tree

ordered_tree :: ordered_tree

Manipulation and presentation of ordered trees.
module parser

nitc :: parser

Parser.
module parser_nodes

nitc :: parser_nodes

AST nodes of the Nit language
module parser_prod

nitc :: parser_prod

Production AST nodes full definition.
module parser_work

nitc :: parser_work

Internal algorithm and data structures for the Nit parser
module phase

nitc :: phase

Phases of the processing of nit programs
module poset

poset :: poset

Pre order sets and partial order set (ie hierarchies)
module protocol

core :: protocol

module queue

core :: queue

Queuing data structures and wrappers
module range

core :: range

Module for range of discrete objects.
module re

core :: re

Regular expression support for all services based on Pattern
module ropes

core :: ropes

Tree-based representation of a String.
module scope

nitc :: scope

Identification and scoping of local variables and labels.
module serialization

serialization :: serialization

General serialization services
module serialization_core

serialization :: serialization_core

Abstract services to serialize Nit objects to different formats
module serialization_write

json :: serialization_write

Services to write Nit objects to JSON strings: serialize_to_json and JsonSerializer
module sorter

core :: sorter

This module contains classes used to compare things and sorts arrays.
module stream

core :: stream

Input and output streams of characters
module tables

nitc :: tables

Module that interfaces the parsing tables.
module template

template :: template

Basic template system
module text

core :: text

All the classes and methods related to the manipulation of text entities
module time

core :: time

Management of time and dates
module toolcontext

nitc :: toolcontext

Common command-line tool infrastructure than handle options and error messages
module typing

nitc :: typing

Intraprocedural resolution of static types and OO-services
module union_find

core :: union_find

union–find algorithm using an efficient disjoint-set data structure
module utf8

core :: utf8

Codec for UTF-8 I/O
module version

nitc :: version

This file was generated by git-gen-version.sh

Parents

module explain_assert_api

nitc :: explain_assert_api

Explain failed assert to the console (service declaration only)
module mixin

nitc :: mixin

Loading and additional module refinements at link-time.
module semantize

nitc :: semantize

Process bodies of methods in regard with the model.
module serialize_model

nitc :: serialize_model

Service to serialize POSet to JSON

Children

module on_demand_compiler

nitc :: on_demand_compiler

Compiles extern code within a module to a static library, as needed
module virtual_machine

nitc :: virtual_machine

Implementation of the Nit virtual machine

Descendants

module a_star-m

a_star-m

module compilation

nitc :: compilation

The compilation module of the VirtualMachine
module dynamic_loading_ffi

nitc :: dynamic_loading_ffi

Execute FFI code by creating and loading shared libraries
module interpreter

nitc :: interpreter

Interpretation of Nit programs
module nit

nitc :: nit

A naive Nit interpreter
module nitvm

nitc :: nitvm

The Nit virtual machine launcher
module test_astbuilder

nitc :: test_astbuilder

Program used to test the clone method of the astbuilder tool
module variables_numbering

nitc :: variables_numbering

Handle all numbering operations related to local variables in the Nit virtual machine
module vm

nitc :: vm

Entry point of all vm components
module vm_optimizations

nitc :: vm_optimizations

Optimization of the nitvm
# Interpretation of a Nit program directly on the AST
module naive_interpreter

import literal
import semantize
private import parser::tables
import mixin
private import model::serialize_model
private import frontend::explain_assert_api

redef class ToolContext
	# --discover-call-trace
	var opt_discover_call_trace = new OptionBool("Trace calls of the first invocation of methods", "--discover-call-trace")

	redef init
	do
		super
		self.option_context.add_option(self.opt_discover_call_trace)
	end
end

redef class ModelBuilder
	# Execute the program from the entry point (`Sys::main`) of the `mainmodule`
	# `arguments` are the command-line arguments in order
	# REQUIRE that:
	#   1. the AST is fully loaded.
	#   2. the model is fully built.
	#   3. the instructions are fully analysed.
	fun run_naive_interpreter(mainmodule: MModule, arguments: Array[String])
	do
		var time0 = get_time
		self.toolcontext.info("*** START INTERPRETING ***", 1)

		var interpreter = new NaiveInterpreter(self, mainmodule, arguments)
		interpreter.start(mainmodule)

		var time1 = get_time
		self.toolcontext.info("*** END INTERPRETING: {time1-time0} ***", 2)
	end
end

# The visitor that interprets the Nit Program by walking on the AST
class NaiveInterpreter
	# The modelbuilder that know the AST and its associations with the model
	var modelbuilder: ModelBuilder

	# The main module of the program (used to lookup method)
	var mainmodule: MModule is writable

	# The command line arguments of the interpreted program
	# arguments.first is the program name
	# arguments[1] is the first argument
	var arguments: Array[String]

	# The main Sys instance
	var mainobj: nullable Instance is noinit

	# Name of all supported functional names
	var routine_types: Set[String] = new HashSet[String]

	init
	do
		if mainmodule.model.get_mclasses_by_name("Bool") != null then
			self.true_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, true)
			init_instance_primitive(self.true_instance)
			self.false_instance = new PrimitiveInstance[Bool](mainmodule.bool_type, false)
			init_instance_primitive(self.false_instance)
		end
		self.null_instance = new PrimitiveInstance[nullable Object](mainmodule.model.null_type, null)

		routine_types.add("RoutineRef")
		for name in ["Proc", "Fun", "ProcRef", "FunRef"] do
			# 20 is a magic number = upper limit of the arity of each functional class.
			# i.e. Proc0, Proc1, ... Proc19
			for i  in [0..20[ do
				routine_types.add("{name}{i}")
			end
		end
	end

	# Starts the interpreter on the main module of a program
	fun start(mainmodule: MModule) do
		var interpreter = self
		var sys_type = mainmodule.sys_type
		if sys_type == null then return # no class Sys
		var mainobj = new MutableInstance(sys_type)
		interpreter.mainobj = mainobj
		interpreter.init_instance(mainobj)
		var initprop = mainmodule.try_get_primitive_method("init", sys_type.mclass)
		if initprop != null then
			interpreter.send(initprop, [mainobj])
		end
		var mainprop = mainmodule.try_get_primitive_method("run", sys_type.mclass) or else
			mainmodule.try_get_primitive_method("main", sys_type.mclass)
		if mainprop != null then
			interpreter.send(mainprop, [mainobj])
		end
	end

	# Subtype test in the context of the mainmodule
	fun is_subtype(sub, sup: MType): Bool
	do
		return sub.is_subtype(self.mainmodule, current_receiver_class, sup)
	end

	# Get a primitive method in the context of the main module
	fun force_get_primitive_method(name: String, recv: MType): MMethod
	do
		assert recv isa MClassType
		return self.modelbuilder.force_get_primitive_method(current_node, name, recv.mclass, self.mainmodule)
	end

	# Is a return, a break or a continue executed?
	# Set this mark to skip the evaluation until a labeled statement catch it with `is_escape`
	var escapemark: nullable EscapeMark = null

	# The count of `catch` blocs that have been encountered and can catch an abort
	var catch_count = 0 is writable

	# The last error thrown on abort/runtime error where catch_count > 0
	var last_error: nullable FatalError = null

	# Is a return or a break or a continue executed?
	# Use this function to know if you must skip the evaluation of statements
	fun is_escaping: Bool do return escapemark != null

	# The value associated with the current return/break/continue, if any.
	# Set the value when you set a escapemark.
	# Read the value when you catch a mark or reach the end of a method
	var escapevalue: nullable Instance = null

	# If there is a break/continue and is associated with `escapemark`, then return true and clear the mark.
	# If there is no break/continue or if `escapemark` is null then return false.
	# Use this function to catch a potential break/continue.
	fun is_escape(escapemark: nullable EscapeMark): Bool
	do
		if escapemark != null and self.escapemark == escapemark then
			self.escapemark = null
			return true
		else
			return false
		end
	end

	# Evaluate `n` as an expression in the current context.
	# Return the value of the expression.
	# If `n` cannot be evaluated, then aborts.
	fun expr(n: AExpr): nullable Instance
	do
		var frame = self.frame
		var old = frame.current_node
		frame.current_node = n
		#n.debug("IN Execute expr")
		var i = n.expr(self)
		if i == null and not self.is_escaping then
			n.debug("inconsitance: no value and not escaping.")
		end
		var implicit_cast_to = n.implicit_cast_to
		if i != null and implicit_cast_to != null then
			var mtype = self.unanchor_type(implicit_cast_to)
			if not self.is_subtype(i.mtype, mtype) then n.fatal(self, "Cast failed. Expected `{implicit_cast_to}`, got `{i.mtype}`")
		end

		#n.debug("OUT Execute expr: value is {i}")
		#if not is_subtype(i.mtype, n.mtype.as(not null)) then n.debug("Expected {n.mtype.as(not null)} got {i}")
		frame.current_node = old
		return i
	end

	# Evaluate `n` as a statement in the current context.
	# Do nothing if `n` is null.
	# If `n` cannot be evaluated, then aborts.
	fun stmt(n: nullable AExpr)
	do
		if n == null then return

		if n.comprehension != null then
			var comprehension = frame.comprehension.as(not null)
			var i = expr(n)
			if i != null then comprehension.add(i)
			return
		end

		var frame = self.frame
		var old = frame.current_node
		frame.current_node = n
		n.stmt(self)
		frame.current_node = old
	end

	# Map used to store values of nodes that must be evaluated once in the system (`AOnceExpr`)
	var onces: Map[ANode, Instance] = new HashMap[ANode, Instance]

	# Return the boolean instance associated with `val`.
	fun bool_instance(val: Bool): Instance
	do
		if val then return self.true_instance else return self.false_instance
	end

	# Return the integer instance associated with `val`.
	fun int_instance(val: Int): Instance
	do
		var t = mainmodule.int_type
		var instance = new PrimitiveInstance[Int](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# Return the byte instance associated with `val`.
	fun byte_instance(val: Byte): Instance
	do
		var t = mainmodule.byte_type
		var instance = new PrimitiveInstance[Byte](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# Return the int8 instance associated with `val`.
	fun int8_instance(val: Int8): Instance
	do
		var t = mainmodule.int8_type
		var instance = new PrimitiveInstance[Int8](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# Return the int16 instance associated with `val`.
	fun int16_instance(val: Int16): Instance
	do
		var t = mainmodule.int16_type
		var instance = new PrimitiveInstance[Int16](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# Return the uint16 instance associated with `val`.
	fun uint16_instance(val: UInt16): Instance
	do
		var t = mainmodule.uint16_type
		var instance = new PrimitiveInstance[UInt16](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# Return the int32 instance associated with `val`.
	fun int32_instance(val: Int32): Instance
	do
		var t = mainmodule.int32_type
		var instance = new PrimitiveInstance[Int32](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# Return the uint32 instance associated with `val`.
	fun uint32_instance(val: UInt32): Instance
	do
		var t = mainmodule.uint32_type
		var instance = new PrimitiveInstance[UInt32](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# Return the char instance associated with `val`.
	fun char_instance(val: Char): Instance
	do
		var t = mainmodule.char_type
		var instance = new PrimitiveInstance[Char](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# Return the float instance associated with `val`.
	fun float_instance(val: Float): Instance
	do
		var t = mainmodule.float_type
		var instance = new PrimitiveInstance[Float](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# The unique instance of the `true` value.
	var true_instance: Instance is noinit

	# The unique instance of the `false` value.
	var false_instance: Instance is noinit

	# The unique instance of the `null` value.
	var null_instance: Instance is noinit

	# Return a new array made of `values`.
	# The dynamic type of the result is Array[elttype].
	fun array_instance(values: Array[Instance], elttype: MType): Instance
	do
		assert not elttype.need_anchor
		var nat = new PrimitiveInstance[Array[Instance]](mainmodule.native_array_type(elttype), values)
		init_instance_primitive(nat)
		var mtype = mainmodule.array_type(elttype)
		var res = new MutableInstance(mtype)
		self.init_instance(res)
		self.send(self.force_get_primitive_method("with_native", mtype), [res, nat, self.int_instance(values.length)])
		return res
	end

	# Return a instance associated to a primitive class
	# Current primitive classes are `Int`, `Bool`, and `String`
	fun value_instance(object: Object): Instance
	do
		if object isa Int then
			return int_instance(object)
		else if object isa Bool then
			return bool_instance(object)
		else if object isa String then
			return string_instance(object)
		else
			abort
		end
	end

	# Return a new C string initialized with `txt`
	fun c_string_instance(txt: String): Instance
	do
		var instance = c_string_instance_len(txt.byte_length+1)
		var val = instance.val
		val[txt.byte_length] = 0
		txt.to_cstring.copy_to(val, txt.byte_length, 0, 0)

		return instance
	end

	# Return a new C string initialized with `txt`
	fun c_string_instance_from_ns(txt: CString, len: Int): Instance
	do
		var instance = c_string_instance_len(len)
		var val = instance.val
		txt.copy_to(val, len, 0, 0)

		return instance
	end

	# Return a new C string instance sharing the same data space as `txt`
	fun c_string_instance_fast_cstr(txt: CString, from: Int): Instance
	do
		var ncstr = txt.fast_cstring(from)
		var t = mainmodule.c_string_type

		var instance = new PrimitiveInstance[CString](t, ncstr)
		init_instance_primitive(instance)

		return instance
	end

	# Return a new C string initialized of `length`
	fun c_string_instance_len(length: Int): PrimitiveInstance[CString]
	do
		var val = new CString(length)

		var t = mainmodule.c_string_type
		var instance = new PrimitiveInstance[CString](t, val)
		init_instance_primitive(instance)
		return instance
	end

	# Return a new String instance for `txt`
	fun string_instance(txt: String): Instance
	do
		var nat = c_string_instance(txt)
		var res = self.send(self.force_get_primitive_method("to_s_unsafe", nat.mtype), [nat, self.int_instance(txt.byte_length), self.int_instance(txt.length), self.false_instance, self.false_instance])
		assert res != null
		return res
	end

	# The virtual type of the frames used in the execution engine
	type FRAME: Frame

	# The current frame used to store local variables of the current method executed
	fun frame: FRAME do return frames.first

	# The stack of all frames. The first one is the current one.
	var frames = new List[FRAME]

	# Return a stack trace. One line per function
	fun stack_trace: String
	do
		var b = new FlatBuffer
		b.append(",---- Stack trace -- - -  -\n")
		for f in frames do
			b.append("| {f.mpropdef} ({f.current_node.location})\n")
		end
		b.append("`------------------- - -  -")
		return b.to_s
	end

	# The current node, used to print errors, debug and stack-traces
	fun current_node: nullable ANode
	do
		if frames.is_empty then return null
		return frames.first.current_node
	end

	# The dynamic type of the current `self`
	fun current_receiver_class: MClassType
	do
		return frames.first.arguments.first.mtype.as(MClassType)
	end

	# Initialize the environment for a call and return a new Frame
	# *`node` The AST node
	# *`mpropdef` The corresponding mpropdef
	# *`args` Arguments of the call
	fun new_frame(node: ANode, mpropdef: MPropDef, args: Array[Instance]): FRAME
	do
		return new InterpreterFrame(node, mpropdef, args)
	end

	# Exit the program with a message
	fun fatal(message: String)
	do
		var node = current_node
		if node == null then
			print message
		else
			node.fatal(self, message)
		end
		exit(1)
	end

	# Debug on the current node
	fun debug(message: String)
	do
		var node = current_node
		if node == null then
			print message
		else
			node.debug(message)
		end
	end

	# Retrieve the value of the variable in the current frame
	fun read_variable(v: Variable): Instance
	do
		var f = frames.first.as(InterpreterFrame)
		return f.map[v]
	end

	# Assign the value of the variable in the current frame
	fun write_variable(v: Variable, value: Instance)
	do
		var f = frames.first.as(InterpreterFrame)
		f.map[v] = value
	end

	# Store known methods, used to trace methods as they are reached
	var discover_call_trace: Set[MMethodDef] = new HashSet[MMethodDef]

	# Consumes an iterator of expressions and tries to map each element to
	# its corresponding Instance.
	#
	# If any AExprs doesn't resolve to an Instance, then it returns null.
	# Otherwise return an array of instances
	fun aexprs_to_instances(aexprs: Iterator[AExpr]): nullable Array[Instance]
	do
		var accumulator = new Array[Instance]
		for aexpr in aexprs do
			var instance = expr(aexpr)
			if instance == null then return null
			accumulator.push(instance)
		end
		return accumulator
	end

	# Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
	# This method is used to manage varargs in signatures and returns the real array
	# of instances to use in the call.
	# Return `null` if one of the evaluation of the arguments return null.
	fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: Instance, args: SequenceRead[AExpr]): nullable Array[Instance]
	do
		var msignature = mpropdef.msignature.as(not null)
		var res = new Array[Instance]
		res.add(recv)

		if msignature.arity == 0 then return res

		if map == null then
			assert args.length == msignature.arity else debug("Expected {msignature.arity} args, got {args.length}")
			var rest_args = aexprs_to_instances(args.iterator)
			if rest_args == null then return null
			res.append(rest_args)
			return res
		end

		# Eval in order of arguments, not parameters
		var exprs = aexprs_to_instances(args.iterator)
		if exprs == null then return null

		# Fill `res` with the result of the evaluation according to the mapping
		for i in [0..msignature.arity[ do
			var param = msignature.mparameters[i]
			var j = map.map.get_or_null(i)
			if j == null then
				# default value
				res.add(null_instance)
				continue
			end
			if param.is_vararg and args[i].vararg_decl > 0 then
				var vararg = exprs.sub(j, args[i].vararg_decl)
				var elttype = param.mtype.anchor_to(self.mainmodule, recv.mtype.as(MClassType))
				var arg = self.array_instance(vararg, elttype)
				res.add(arg)
				continue
			end
			res.add exprs[j]
		end
		return res
	end

	# Execute `mpropdef` for a `args` (where `args[0]` is the receiver).
	# Return a value if `mpropdef` is a function, or null if it is a procedure.
	# The call is direct/static. There is no message-sending/late-binding.
	fun call(mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
	do
		if self.modelbuilder.toolcontext.opt_discover_call_trace.value and not self.discover_call_trace.has(mpropdef) then
			self.discover_call_trace.add mpropdef
			self.debug("Discovered {mpropdef}")
		end
		assert args.length == mpropdef.msignature.arity + 1 else debug("Invalid arity for {mpropdef}. {args.length} arguments given.")

		# Look for the AST node that implements the property
		var val = mpropdef.constant_value

		var node = modelbuilder.mpropdef2node(mpropdef)
		if mpropdef.is_abstract then
			if node != null then
				self.frames.unshift new_frame(node, mpropdef, args)
			end
			fatal("Abstract method `{mpropdef.mproperty.name}` called on `{args.first.mtype}`")
			abort
		end

		if node isa APropdef then
			self.parameter_check(node, mpropdef, args)
			return node.call(self, mpropdef, args)
		else if node isa AClassdef then
			self.parameter_check(node, mpropdef, args)
			return node.call(self, mpropdef, args)
		else if node != null then
			fatal("Fatal Error: method {mpropdef} associated to unexpected AST node {node.location}")
			abort
		else if val != null then
			return value_instance(val)
		else
			fatal("Fatal Error: method {mpropdef} not found in the AST")
			abort
		end
	end

	# Execute type checks of covariant parameters
	fun parameter_check(node: ANode, mpropdef: MMethodDef, args: Array[Instance])
	do
		var msignature = mpropdef.msignature.as(not null)
		for i in [0..msignature.arity[ do
			var mp = msignature.mparameters[i]

			# skip test for vararg since the array is instantiated with the correct polymorphic type
			if mp.is_vararg then continue

			# skip if the cast is not required
			var origmtype =  mpropdef.mproperty.intro.msignature.mparameters[i].mtype
			if not origmtype.need_anchor then continue

			#print "{mpropdef}: {mpropdef.mproperty.intro.msignature.mparameters[i]}"

			# get the parameter type
			var mtype = mp.mtype
			var anchor = args.first.mtype.as(MClassType)
			var amtype = mtype.anchor_to(self.mainmodule, anchor)
			if not args[i+1].mtype.is_subtype(self.mainmodule, anchor, amtype) then
				node.fatal(self, "Cast failed. Expected `{mtype}`, got `{args[i+1].mtype}`")
			end
		end
	end

	# Common code for runtime injected calls and normal calls
	fun send_commons(mproperty: MMethod, args: Array[Instance], mtype: MType): nullable Instance
	do
		if mtype isa MNullType then
			if mproperty.name == "==" or mproperty.name == "is_same_instance" then
				return self.bool_instance(args[0] == args[1])
			else if mproperty.name == "!=" then
				return self.bool_instance(args[0] != args[1])
			end
			#fatal("Receiver is null. {mproperty}. {args.join(" ")} {self.frame.current_node.class_name}")
			fatal("Receiver is null")
		end
		return null
	end

	# Execute a full `callsite` for given `args`
	# Use this method, instead of `send` to execute and control the additional behavior of the call-sites
	fun callsite(callsite: nullable CallSite, arguments: Array[Instance]): nullable Instance
	do
		if callsite == null then return null
		return send(callsite.mproperty, arguments)
	end

	# Execute `mproperty` for a `args` (where `args[0]` is the receiver).
	# Return a value if `mproperty` is a function, or null if it is a procedure.
	# The call is polymorphic. There is a message-sending/late-binding according to the receiver (args[0]).
	fun send(mproperty: MMethod, args: Array[Instance]): nullable Instance
	do
		var recv = args.first
		var mtype = recv.mtype
		var ret = send_commons(mproperty, args, mtype)
		if ret != null then return ret
		var propdef = mproperty.lookup_first_definition(self.mainmodule, mtype)
		return self.call(propdef, args)
	end

	# Read the attribute `mproperty` of an instance `recv` and return its value.
	# If the attribute in not yet initialized, then aborts with an error message.
	fun read_attribute(mproperty: MAttribute, recv: Instance): Instance
	do
		assert recv isa MutableInstance
		if not recv.attributes.has_key(mproperty) then
			fatal("Uninitialized attribute {mproperty.name}")
			abort
		end
		return recv.attributes[mproperty]
	end

	# Replace in `recv` the value of the attribute `mproperty` by `value`
	fun write_attribute(mproperty: MAttribute, recv: Instance, value: Instance)
	do
		assert recv isa MutableInstance
		recv.attributes[mproperty] = value
	end

	# Is the attribute `mproperty` initialized the instance `recv`?
	fun isset_attribute(mproperty: MAttribute, recv: Instance): Bool
	do
		assert recv isa MutableInstance
		return recv.attributes.has_key(mproperty)
	end

	# Collect attributes of a type in the order of their init
	fun collect_attr_propdef(mtype: MType): Array[AAttrPropdef]
	do
		var cache = self.collect_attr_propdef_cache
		if cache.has_key(mtype) then return cache[mtype]

		var res = new Array[AAttrPropdef]
		var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
		self.mainmodule.linearize_mclassdefs(cds)
		for cd in cds do
			res.add_all(modelbuilder.collect_attr_propdef(cd))
		end

		cache[mtype] = res
		return res
	end

	private var collect_attr_propdef_cache = new HashMap[MType, Array[AAttrPropdef]]

	# Fill the initial values of the newly created instance `recv`.
	# `recv.mtype` is used to know what must be filled.
	fun init_instance(recv: Instance)
	do
		for npropdef in collect_attr_propdef(recv.mtype) do
			npropdef.init_expr(self, recv)
		end
	end

	# A hook to initialize a `PrimitiveInstance`
	fun init_instance_primitive(recv: Instance) do end

	# This function determines the correct type according to the receiver of the current propdef (self).
	fun unanchor_type(mtype: MType): MType
	do
		return mtype.anchor_to(self.mainmodule, current_receiver_class)
	end

	# Placebo instance used to mark internal error result when `null` already have a meaning.
	# TODO: replace with multiple return or something better
	var error_instance = new MutableInstance(modelbuilder.model.null_type) is lazy
end

# A runtime error
class FatalError
	# The error message
	var message: String

	# The problematic node, if any
	var node: nullable ANode
end

# An instance represents a value of the executed program.
abstract class Instance
	# The dynamic type of the instance
	# ASSERT: not self.mtype.is_anchored
	var mtype: MType

	# Return `true` if the instance is the `true` value.
	#
	# Return `false` if the instance is the `false` value.
	# Abort if the instance is not a boolean value.
	fun is_true: Bool do abort

	# Return `true` if the instance is null.
	# Return `false` otherwise.
	fun is_null: Bool do return mtype isa MNullType

	# Return true if `self` IS `o` (using the Nit semantic of is)
	fun eq_is(o: Instance): Bool do return self.is_same_instance(o)

	# Human readable object identity "Type#number"
	redef fun to_s do return "{mtype}"

	# Return the integer value if the instance is an integer.
	# else aborts
	fun to_i: Int do abort

	# Return the integer value if the instance is a float.
	# else aborts
	fun to_f: Float do abort

	# Return the integer value if the instance is a byte.
	# else aborts
	fun to_b: Byte do abort

	# Return the integer value if the instance is a int8.
	# else aborts
	fun to_i8: Int8 do abort

	# Return the integer value if the instance is a int16.
	# else aborts
	fun to_i16: Int16 do abort

	# Return the integer value if the instance is a uint16.
	# else aborts
	fun to_u16: UInt16 do abort

	# Return the integer value if the instance is a int32.
	# else aborts
	fun to_i32: Int32 do abort

	# Return the integer value if the instance is a uint32.
	# else aborts
	fun to_u32: UInt32 do abort

	# The real value encapsulated if the instance is primitive.
	# Else aborts.
	fun val: nullable Object do abort
end

# A instance with attribute (standards objects)
class MutableInstance
	super Instance

	# The values of the attributes
	var attributes: Map[MAttribute, Instance] = new HashMap[MAttribute, Instance]
end

# An instance with the original receiver and callsite (for function reference)
class CallrefInstance
	super Instance

	# The original receiver
	#
	# ~~~nitish
	# var a = new A
	# var f = &a.toto # `a` is the original receiver
	# ~~~
	var recv: Instance

	# The original callsite
	#
	# ~~~nitish
	# var a = new A
	# var f = &a.toto # `toto` is the original callsite
	# ~~~
	var callsite: CallSite
end

# Special instance to handle primitives values (int, bool, etc.)
# The trick is just to encapsulate the “real” value.
class PrimitiveInstance[E]
	super Instance

	# The real value encapsulated
	redef var val: E

	redef fun is_true
	do
		if val == true then return true
		if val == false then return false
		abort
	end

	redef fun ==(o)
	do
		if not o isa PrimitiveInstance[nullable Object] then return false
		return self.val == o.val
	end

	redef fun eq_is(o)
	do
		if not o isa PrimitiveInstance[nullable Object] then return false
		return self.val.is_same_instance(o.val)
	end

	redef fun to_s do return "{mtype}#{val.object_id}({val or else "null"})"

	redef fun to_i do return val.as(Int)

	redef fun to_f do return val.as(Float)

	redef fun to_b do return val.as(Byte)

	redef fun to_i8 do return val.as(Int8)

	redef fun to_i16 do return val.as(Int16)

	redef fun to_u16 do return val.as(UInt16)

	redef fun to_i32 do return val.as(Int32)

	redef fun to_u32 do return val.as(UInt32)
end

# Information about local variables in a running method
abstract class Frame
	# The current visited node
	# The node is stored by frame to keep a stack trace
	var current_node: ANode
	# The executed property.
	# A Method in case of a call, an attribute in case of a default initialization.
	var mpropdef: MPropDef
	# Arguments of the method (the first is the receiver)
	var arguments: Array[Instance]
	# Indicate if the expression has an array comprehension form
	var comprehension: nullable Array[Instance] = null
end

# Implementation of a Frame with a Hashmap to store local variables
class InterpreterFrame
	super Frame

	# Mapping between a variable and the current value
	var map: Map[Variable, Instance] = new HashMap[Variable, Instance]
end

redef class ANode
	# Aborts the program with a message
	# `v` is used to know if a colored message is displayed or not
	fun fatal(v: NaiveInterpreter, message: String)
	do
		# Abort if there is a `catch` block
		if v.catch_count > 0 then
			v.last_error = new FatalError(message, self)
			abort
		end

		if v.modelbuilder.toolcontext.opt_no_color.value then
			sys.stderr.write("Runtime error: {message} ({location.file.filename}:{location.line_start})\n")
		else
			sys.stderr.write("{location}: Runtime error: {message}\n{location.colored_line("0;31")}\n")
			sys.stderr.write(v.stack_trace)
			sys.stderr.write("\n")
		end
		exit(1)
	end
end

redef class APropdef
	# Execute a `mpropdef` associated with the current node.
	private fun call(v: NaiveInterpreter, mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
	do
		fatal(v, "NOT YET IMPLEMENTED method kind {class_name}. {mpropdef}")
		abort
	end
end

redef class AMethPropdef
	super TablesCapable

	redef fun call(v, mpropdef, args)
	do
		var f = v.new_frame(self, mpropdef, args)
		var res = call_commons(v, mpropdef, args, f)
		v.frames.shift
		if v.is_escape(self.return_mark) then
			res = v.escapevalue
			return res
		end
		return res
	end

	# Execution of the body of the method
	#
	# It handle the common special cases: super, intern, extern
	fun call_commons(v: NaiveInterpreter, mpropdef: MMethodDef, arguments: Array[Instance], f: Frame): nullable Instance
	do
		v.frames.unshift(f)

		for i in [0..mpropdef.msignature.arity[ do
			var variable = self.n_signature.n_params[i].variable
			assert variable != null
			v.write_variable(variable, arguments[i+1])
		end

		# Call the implicit super-init
		var auto_super_inits = self.auto_super_inits
		if auto_super_inits != null then
			var args = [arguments.first]
			for auto_super_init in auto_super_inits do
				args.clear
				for i in [0..auto_super_init.msignature.arity+1[ do
					args.add(arguments[i])
				end
				assert auto_super_init.mproperty != mpropdef.mproperty
				v.callsite(auto_super_init, args)
			end
		end
		if auto_super_call then
			# standard call-next-method
			var superpd = mpropdef.lookup_next_definition(v.mainmodule, arguments.first.mtype)
			v.call(superpd, arguments)
		end

		# First, try intern
		if mpropdef.is_intern or mpropdef.is_extern then
			var res = intern_call(v, mpropdef, arguments)
			if res != v.error_instance then return res
		end
		# Then, try extern
		if mpropdef.is_extern then
			var res = call_extern(v, mpropdef, arguments, f)
			if res != v.error_instance then return res
		end
		# Else try block
		if n_block != null then
			v.stmt(self.n_block)
			return null
		end

		# Fail if nothing succeed
		if mpropdef.is_intern then
			fatal(v, "NOT YET IMPLEMENTED intern {mpropdef}")
		else if mpropdef.is_extern then
			fatal(v, "NOT YET IMPLEMENTED extern {mpropdef}")
		else
			fatal(v, "NOT YET IMPLEMENTED <wat?> {mpropdef}")
		end
		abort
	end

	# Call this extern method
	protected fun call_extern(v: NaiveInterpreter, mpropdef: MMethodDef, arguments: Array[Instance], f: Frame): nullable Instance
	do
		return v.error_instance
	end

	# Interprets a intern or a shortcut extern method.
	# Returns the result for a function, `null` for a procedure, or `error_instance` if the method is unknown.
	private fun intern_call(v: NaiveInterpreter, mpropdef: MMethodDef, args: Array[Instance]): nullable Instance
	do
		var pname = mpropdef.mproperty.name
		var cname = mpropdef.mclassdef.mclass.name

		if pname == "call" and v.routine_types.has(cname) then
			var routine = args.shift
			assert routine isa CallrefInstance
			# Swap the receiver position with the original recv of the call form.
			args.unshift routine.recv
			var res = v.callsite(routine.callsite, args)
			# recover the old args state
			args.shift
			args.unshift routine
			return res
		end

		if pname == "output" then
			var recv = args.first
			recv.val.output
			return null
		else if pname == "object_id" then
			var recv = args.first
			if recv isa PrimitiveInstance[Object] then
				return v.int_instance(recv.val.object_id)
			else
				return v.int_instance(recv.object_id)
			end
		else if pname == "output_class_name" then
			var recv = args.first
			print recv.mtype
			return null
		else if pname == "native_class_name" then
			var recv = args.first
			var txt = recv.mtype.to_s
			return v.c_string_instance(txt)
		else if pname == "==" then
			# == is correctly redefined for instances
			return v.bool_instance(args[0] == args[1])
		else if pname == "!=" then
			return v.bool_instance(args[0] != args[1])
		else if pname == "is_same_type" then
			return v.bool_instance(args[0].mtype == args[1].mtype)
		else if pname == "is_same_instance" then
			return v.bool_instance(args[0].eq_is(args[1]))
		else if pname == "class_inheritance_metamodel_json" then
			return v.c_string_instance(v.mainmodule.flatten_mclass_hierarchy.to_thin_json)
		else if pname == "exit" then
			exit(args[1].to_i)
			abort
		else if pname == "buffer_mode_full" then
			return v.int_instance(sys.buffer_mode_full)
		else if pname == "buffer_mode_line" then
			return v.int_instance(sys.buffer_mode_line)
		else if pname == "buffer_mode_none" then
			return v.int_instance(sys.buffer_mode_none)
		else if pname == "sys" then
			return v.mainobj
		else if cname == "Int" then
			var recvval = args[0].to_i
			if pname == "unary -" then
				return v.int_instance(-recvval)
			else if pname == "unary +" then
				return args[0]
			else if pname == "+" then
				return v.int_instance(recvval + args[1].to_i)
			else if pname == "-" then
				return v.int_instance(recvval - args[1].to_i)
			else if pname == "*" then
				return v.int_instance(recvval * args[1].to_i)
			else if pname == "%" then
				return v.int_instance(recvval % args[1].to_i)
			else if pname == "/" then
				return v.int_instance(recvval / args[1].to_i)
			else if pname == "<" then
				return v.bool_instance(recvval < args[1].to_i)
			else if pname == ">" then
				return v.bool_instance(recvval > args[1].to_i)
			else if pname == "<=" then
				return v.bool_instance(recvval <= args[1].to_i)
			else if pname == ">=" then
				return v.bool_instance(recvval >= args[1].to_i)
			else if pname == "<=>" then
				return v.int_instance(recvval <=> args[1].to_i)
			else if pname == "&" then
				return v.int_instance(recvval & args[1].to_i)
			else if pname == "|" then
				return v.int_instance(recvval | args[1].to_i)
			else if pname == "to_f" then
				return v.float_instance(recvval.to_f)
			else if pname == "to_b" then
				return v.byte_instance(recvval.to_b)
			else if pname == "<<" then
				return v.int_instance(recvval << args[1].to_i)
			else if pname == ">>" then
				return v.int_instance(recvval >> args[1].to_i)
			else if pname == "to_i8" then
				return v.int8_instance(recvval.to_i8)
			else if pname == "to_i16" then
				return v.int16_instance(recvval.to_i16)
			else if pname == "to_u16" then
				return v.uint16_instance(recvval.to_u16)
			else if pname == "to_i32" then
				return v.int32_instance(recvval.to_i32)
			else if pname == "to_u32" then
				return v.uint32_instance(recvval.to_u32)
			end
		else if cname == "Byte" then
			var recvval = args[0].to_b
			if pname == "unary -" then
				return v.byte_instance(-recvval)
			else if pname == "unary +" then
				return args[0]
			else if pname == "+" then
				return v.byte_instance(recvval + args[1].to_b)
			else if pname == "-" then
				return v.byte_instance(recvval - args[1].to_b)
			else if pname == "*" then
				return v.byte_instance(recvval * args[1].to_b)
			else if pname == "%" then
				return v.byte_instance(recvval % args[1].to_b)
			else if pname == "/" then
				return v.byte_instance(recvval / args[1].to_b)
			else if pname == "<" then
				return v.bool_instance(recvval < args[1].to_b)
			else if pname == ">" then
				return v.bool_instance(recvval > args[1].to_b)
			else if pname == "<=" then
				return v.bool_instance(recvval <= args[1].to_b)
			else if pname == ">=" then
				return v.bool_instance(recvval >= args[1].to_b)
			else if pname == "<=>" then
				return v.int_instance(recvval <=> args[1].to_b)
			else if pname == "&" then
				return v.byte_instance(recvval & args[1].to_b)
			else if pname == "|" then
				return v.byte_instance(recvval | args[1].to_b)
			else if pname == "to_f" then
				return v.float_instance(recvval.to_f)
			else if pname == "to_i" then
				return v.int_instance(recvval.to_i)
			else if pname == "<<" then
				return v.byte_instance(recvval << args[1].to_i)
			else if pname == ">>" then
				return v.byte_instance(recvval >> args[1].to_i)
			else if pname == "to_i8" then
				return v.int8_instance(recvval.to_i8)
			else if pname == "to_i16" then
				return v.int16_instance(recvval.to_i16)
			else if pname == "to_u16" then
				return v.uint16_instance(recvval.to_u16)
			else if pname == "to_i32" then
				return v.int32_instance(recvval.to_i32)
			else if pname == "to_u32" then
				return v.uint32_instance(recvval.to_u32)
			else if pname == "byte_to_s_len" then
				return v.int_instance(recvval.to_s.length)
			end
		else if cname == "Char" then
			var recv = args[0].val.as(Char)
			if pname == "successor" then
				return v.char_instance(recv.successor(args[1].to_i))
			else if pname == "predecessor" then
				return v.char_instance(recv.predecessor(args[1].to_i))
			else if pname == "<" then
				return v.bool_instance(recv < args[1].val.as(Char))
			else if pname == ">" then
				return v.bool_instance(recv > args[1].val.as(Char))
			else if pname == "<=" then
				return v.bool_instance(recv <= args[1].val.as(Char))
			else if pname == ">=" then
				return v.bool_instance(recv >= args[1].val.as(Char))
			else if pname == "<=>" then
				return v.int_instance(recv <=> args[1].val.as(Char))
			end
		else if cname == "Float" then
			var recv = args[0].to_f
			if pname == "unary -" then
				return v.float_instance(-recv)
			else if pname == "unary +" then
				return args[0]
			else if pname == "+" then
				return v.float_instance(recv + args[1].to_f)
			else if pname == "-" then
				return v.float_instance(recv - args[1].to_f)
			else if pname == "*" then
				return v.float_instance(recv * args[1].to_f)
			else if pname == "/" then
				return v.float_instance(recv / args[1].to_f)
			else if pname == "<" then
				return v.bool_instance(recv < args[1].to_f)
			else if pname == ">" then
				return v.bool_instance(recv > args[1].to_f)
			else if pname == "<=" then
				return v.bool_instance(recv <= args[1].to_f)
			else if pname == ">=" then
				return v.bool_instance(recv >= args[1].to_f)
			else if pname == "to_i" then
				return v.int_instance(recv.to_i)
			else if pname == "to_b" then
				return v.byte_instance(recv.to_b)
			else if pname == "to_i8" then
				return v.int8_instance(recv.to_i8)
			else if pname == "to_i16" then
				return v.int16_instance(recv.to_i16)
			else if pname == "to_u16" then
				return v.uint16_instance(recv.to_u16)
			else if pname == "to_i32" then
				return v.int32_instance(recv.to_i32)
			else if pname == "to_u32" then
				return v.uint32_instance(recv.to_u32)
			else if pname == "cos" then
				return v.float_instance(args[0].to_f.cos)
			else if pname == "sin" then
				return v.float_instance(args[0].to_f.sin)
			else if pname == "tan" then
				return v.float_instance(args[0].to_f.tan)
			else if pname == "acos" then
				return v.float_instance(args[0].to_f.acos)
			else if pname == "asin" then
				return v.float_instance(args[0].to_f.asin)
			else if pname == "atan" then
				return v.float_instance(args[0].to_f.atan)
			else if pname == "sqrt" then
				return v.float_instance(args[0].to_f.sqrt)
			else if pname == "exp" then
				return v.float_instance(args[0].to_f.exp)
			else if pname == "log" then
				return v.float_instance(args[0].to_f.log)
			else if pname == "pow" then
				return v.float_instance(args[0].to_f.pow(args[1].to_f))
			else if pname == "abs" then
				return v.float_instance(args[0].to_f.abs)
			else if pname == "hypot_with" then
				return v.float_instance(args[0].to_f.hypot_with(args[1].to_f))
			else if pname == "is_nan" then
				return v.bool_instance(args[0].to_f.is_nan)
			else if pname == "is_inf_extern" then
				return v.bool_instance(args[0].to_f.is_inf != 0)
			else if pname == "round" then
				return v.float_instance(args[0].to_f.round)
			end
		else if cname == "CString" then
			if pname == "new" then
				return v.c_string_instance_len(args[1].to_i)
			end
			var recvval = args.first.val.as(CString)
			if pname == "[]" then
				var arg1 = args[1].to_i
				return v.int_instance(recvval[arg1])
			else if pname == "[]=" then
				var arg1 = args[1].to_i
				recvval[arg1] = args[2].val.as(Int)
				return null
			else if pname == "copy_to" then
				# sig= copy_to(dest: CString, length: Int, from: Int, to: Int)
				var destval = args[1].val.as(CString)
				var lenval = args[2].to_i
				var fromval = args[3].to_i
				var toval = args[4].to_i
				recvval.copy_to(destval, lenval, fromval, toval)
				return null
			else if pname == "atoi" then
				return v.int_instance(recvval.atoi)
			else if pname == "fast_cstring" then
				return v.c_string_instance_fast_cstr(args[0].val.as(CString), args[1].to_i)
			else if pname == "fetch_4_chars" then
				return v.uint32_instance(args[0].val.as(CString).fetch_4_chars(args[1].to_i))
			else if pname == "fetch_4_hchars" then
				return v.uint32_instance(args[0].val.as(CString).fetch_4_hchars(args[1].to_i))
			else if pname == "utf8_length" then
				return v.int_instance(args[0].val.as(CString).utf8_length(args[1].to_i, args[2].to_i))
			end
		else if cname == "NativeArray" then
			if pname == "new" then
				var val = new Array[Instance].filled_with(v.null_instance, args[1].to_i)
				var instance = new PrimitiveInstance[Array[Instance]](args[0].mtype, val)
				v.init_instance_primitive(instance)
				return instance
			end
			var recvval = args.first.val.as(Array[Instance])
			if pname == "[]" then
				return recvval[args[1].to_i]
			else if pname == "[]=" then
				recvval[args[1].to_i] = args[2]
				return null
			else if pname == "length" then
				return v.int_instance(recvval.length)
			else if pname == "copy_to" then
				recvval.copy_to(0, args[2].to_i, args[1].val.as(Array[Instance]), 0)
				return null
			end
		else if cname == "Int8" then
			var recvval = args[0].to_i8
			if pname == "unary -" then
				return v.int8_instance(-recvval)
			else if pname == "unary +" then
				return args[0]
			else if pname == "+" then
				return v.int8_instance(recvval + args[1].to_i8)
			else if pname == "-" then
				return v.int8_instance(recvval - args[1].to_i8)
			else if pname == "*" then
				return v.int8_instance(recvval * args[1].to_i8)
			else if pname == "%" then
				return v.int8_instance(recvval % args[1].to_i8)
			else if pname == "/" then
				return v.int8_instance(recvval / args[1].to_i8)
			else if pname == "<" then
				return v.bool_instance(recvval < args[1].to_i8)
			else if pname == ">" then
				return v.bool_instance(recvval > args[1].to_i8)
			else if pname == "<=" then
				return v.bool_instance(recvval <= args[1].to_i8)
			else if pname == ">=" then
				return v.bool_instance(recvval >= args[1].to_i8)
			else if pname == "<=>" then
				return v.int_instance(recvval <=> args[1].to_i8)
			else if pname == "to_f" then
				return v.float_instance(recvval.to_f)
			else if pname == "to_i" then
				return v.int_instance(recvval.to_i)
			else if pname == "to_b" then
				return v.byte_instance(recvval.to_b)
			else if pname == "to_i16" then
				return v.int16_instance(recvval.to_i16)
			else if pname == "to_u16" then
				return v.uint16_instance(recvval.to_u16)
			else if pname == "to_i32" then
				return v.int32_instance(recvval.to_i32)
			else if pname == "to_u32" then
				return v.uint32_instance(recvval.to_u32)
			else if pname == "<<" then
				return v.int8_instance(recvval << (args[1].to_i))
			else if pname == ">>" then
				return v.int8_instance(recvval >> (args[1].to_i))
			else if pname == "&" then
				return v.int8_instance(recvval & args[1].to_i8)
			else if pname == "|" then
				return v.int8_instance(recvval | args[1].to_i8)
			else if pname == "^" then
				return v.int8_instance(recvval ^ args[1].to_i8)
			else if pname == "unary ~" then
				return v.int8_instance(~recvval)
			end
		else if cname == "Int16" then
			var recvval = args[0].to_i16
			if pname == "unary -" then
				return v.int16_instance(-recvval)
			else if pname == "unary +" then
				return args[0]
			else if pname == "+" then
				return v.int16_instance(recvval + args[1].to_i16)
			else if pname == "-" then
				return v.int16_instance(recvval - args[1].to_i16)
			else if pname == "*" then
				return v.int16_instance(recvval * args[1].to_i16)
			else if pname == "%" then
				return v.int16_instance(recvval % args[1].to_i16)
			else if pname == "/" then
				return v.int16_instance(recvval / args[1].to_i16)
			else if pname == "<" then
				return v.bool_instance(recvval < args[1].to_i16)
			else if pname == ">" then
				return v.bool_instance(recvval > args[1].to_i16)
			else if pname == "<=" then
				return v.bool_instance(recvval <= args[1].to_i16)
			else if pname == ">=" then
				return v.bool_instance(recvval >= args[1].to_i16)
			else if pname == "<=>" then
				return v.int_instance(recvval <=> args[1].to_i16)
			else if pname == "to_f" then
				return v.float_instance(recvval.to_f)
			else if pname == "to_i" then
				return v.int_instance(recvval.to_i)
			else if pname == "to_b" then
				return v.byte_instance(recvval.to_b)
			else if pname == "to_i8" then
				return v.int8_instance(recvval.to_i8)
			else if pname == "to_u16" then
				return v.uint16_instance(recvval.to_u16)
			else if pname == "to_i32" then
				return v.int32_instance(recvval.to_i32)
			else if pname == "to_u32" then
				return v.uint32_instance(recvval.to_u32)
			else if pname == "<<" then
				return v.int16_instance(recvval << (args[1].to_i))
			else if pname == ">>" then
				return v.int16_instance(recvval >> (args[1].to_i))
			else if pname == "&" then
				return v.int16_instance(recvval & args[1].to_i16)
			else if pname == "|" then
				return v.int16_instance(recvval | args[1].to_i16)
			else if pname == "^" then
				return v.int16_instance(recvval ^ args[1].to_i16)
			else if pname == "unary ~" then
				return v.int16_instance(~recvval)
			end
		else if cname == "UInt16" then
			var recvval = args[0].to_u16
			if pname == "unary -" then
				return v.uint16_instance(-recvval)
			else if pname == "unary +" then
				return args[0]
			else if pname == "+" then
				return v.uint16_instance(recvval + args[1].to_u16)
			else if pname == "-" then
				return v.uint16_instance(recvval - args[1].to_u16)
			else if pname == "*" then
				return v.uint16_instance(recvval * args[1].to_u16)
			else if pname == "%" then
				return v.uint16_instance(recvval % args[1].to_u16)
			else if pname == "/" then
				return v.uint16_instance(recvval / args[1].to_u16)
			else if pname == "<" then
				return v.bool_instance(recvval < args[1].to_u16)
			else if pname == ">" then
				return v.bool_instance(recvval > args[1].to_u16)
			else if pname == "<=" then
				return v.bool_instance(recvval <= args[1].to_u16)
			else if pname == ">=" then
				return v.bool_instance(recvval >= args[1].to_u16)
			else if pname == "<=>" then
				return v.int_instance(recvval <=> args[1].to_u16)
			else if pname == "to_f" then
				return v.float_instance(recvval.to_f)
			else if pname == "to_i" then
				return v.int_instance(recvval.to_i)
			else if pname == "to_b" then
				return v.byte_instance(recvval.to_b)
			else if pname == "to_i8" then
				return v.int8_instance(recvval.to_i8)
			else if pname == "to_i16" then
				return v.int16_instance(recvval.to_i16)
			else if pname == "to_i32" then
				return v.int32_instance(recvval.to_i32)
			else if pname == "to_u32" then
				return v.uint32_instance(recvval.to_u32)
			else if pname == "<<" then
				return v.uint16_instance(recvval << (args[1].to_i))
			else if pname == ">>" then
				return v.uint16_instance(recvval >> (args[1].to_i))
			else if pname == "&" then
				return v.uint16_instance(recvval & args[1].to_u16)
			else if pname == "|" then
				return v.uint16_instance(recvval | args[1].to_u16)
			else if pname == "^" then
				return v.uint16_instance(recvval ^ args[1].to_u16)
			else if pname == "unary ~" then
				return v.uint16_instance(~recvval)
			end
		else if cname == "Int32" then
			var recvval = args[0].to_i32
			if pname == "unary -" then
				return v.int32_instance(-recvval)
			else if pname == "unary +" then
				return args[0]
			else if pname == "+" then
				return v.int32_instance(recvval + args[1].to_i32)
			else if pname == "-" then
				return v.int32_instance(recvval - args[1].to_i32)
			else if pname == "*" then
				return v.int32_instance(recvval * args[1].to_i32)
			else if pname == "%" then
				return v.int32_instance(recvval % args[1].to_i32)
			else if pname == "/" then
				return v.int32_instance(recvval / args[1].to_i32)
			else if pname == "<" then
				return v.bool_instance(recvval < args[1].to_i32)
			else if pname == ">" then
				return v.bool_instance(recvval > args[1].to_i32)
			else if pname == "<=" then
				return v.bool_instance(recvval <= args[1].to_i32)
			else if pname == ">=" then
				return v.bool_instance(recvval >= args[1].to_i32)
			else if pname == "<=>" then
				return v.int_instance(recvval <=> args[1].to_i32)
			else if pname == "to_f" then
				return v.float_instance(recvval.to_f)
			else if pname == "to_i" then
				return v.int_instance(recvval.to_i)
			else if pname == "to_b" then
				return v.byte_instance(recvval.to_b)
			else if pname == "to_i8" then
				return v.int8_instance(recvval.to_i8)
			else if pname == "to_i16" then
				return v.int16_instance(recvval.to_i16)
			else if pname == "to_u16" then
				return v.uint16_instance(recvval.to_u16)
			else if pname == "to_u32" then
				return v.uint32_instance(recvval.to_u32)
			else if pname == "<<" then
				return v.int32_instance(recvval << (args[1].to_i))
			else if pname == ">>" then
				return v.int32_instance(recvval >> (args[1].to_i))
			else if pname == "&" then
				return v.int32_instance(recvval & args[1].to_i32)
			else if pname == "|" then
				return v.int32_instance(recvval | args[1].to_i32)
			else if pname == "^" then
				return v.int32_instance(recvval ^ args[1].to_i32)
			else if pname == "unary ~" then
				return v.int32_instance(~recvval)
			end
		else if cname == "UInt32" then
			var recvval = args[0].to_u32
			if pname == "unary -" then
				return v.uint32_instance(-recvval)
			else if pname == "unary +" then
				return args[0]
			else if pname == "+" then
				return v.uint32_instance(recvval + args[1].to_u32)
			else if pname == "-" then
				return v.uint32_instance(recvval - args[1].to_u32)
			else if pname == "*" then
				return v.uint32_instance(recvval * args[1].to_u32)
			else if pname == "%" then
				return v.uint32_instance(recvval % args[1].to_u32)
			else if pname == "/" then
				return v.uint32_instance(recvval / args[1].to_u32)
			else if pname == "<" then
				return v.bool_instance(recvval < args[1].to_u32)
			else if pname == ">" then
				return v.bool_instance(recvval > args[1].to_u32)
			else if pname == "<=" then
				return v.bool_instance(recvval <= args[1].to_u32)
			else if pname == ">=" then
				return v.bool_instance(recvval >= args[1].to_u32)
			else if pname == "<=>" then
				return v.int_instance(recvval <=> args[1].to_u32)
			else if pname == "to_f" then
				return v.float_instance(recvval.to_f)
			else if pname == "to_i" then
				return v.int_instance(recvval.to_i)
			else if pname == "to_b" then
				return v.byte_instance(recvval.to_b)
			else if pname == "to_i8" then
				return v.int8_instance(recvval.to_i8)
			else if pname == "to_i16" then
				return v.int16_instance(recvval.to_i16)
			else if pname == "to_u16" then
				return v.uint16_instance(recvval.to_u16)
			else if pname == "to_i32" then
				return v.int32_instance(recvval.to_i32)
			else if pname == "<<" then
				return v.uint32_instance(recvval << (args[1].to_i))
			else if pname == ">>" then
				return v.uint32_instance(recvval >> (args[1].to_i))
			else if pname == "&" then
				return v.uint32_instance(recvval & args[1].to_u32)
			else if pname == "|" then
				return v.uint32_instance(recvval | args[1].to_u32)
			else if pname == "^" then
				return v.uint32_instance(recvval ^ args[1].to_u32)
			else if pname == "unary ~" then
				return v.uint32_instance(~recvval)
			end
		else if pname == "native_argc" then
			return v.int_instance(v.arguments.length)
		else if pname == "native_argv" then
			var txt = v.arguments[args[1].to_i]
			return v.c_string_instance(txt)
		else if pname == "lexer_goto" then
			return v.int_instance(lexer_goto(args[1].to_i, args[2].to_i))
		else if pname == "lexer_accept" then
			return v.int_instance(lexer_accept(args[1].to_i))
		else if pname == "parser_goto" then
			return v.int_instance(parser_goto(args[1].to_i, args[2].to_i))
		else if pname == "parser_action" then
			return v.int_instance(parser_action(args[1].to_i, args[2].to_i))
		end
		return v.error_instance
	end
end

redef class AAttrPropdef
	redef fun call(v, mpropdef, args)
	do
		var recv = args.first
		assert recv isa MutableInstance
		var attr = self.mpropdef.mproperty
		if mpropdef == mreadpropdef then
			assert args.length == 1
			if not is_lazy or v.isset_attribute(attr, recv) then return v.read_attribute(attr, recv)
			var f = v.new_frame(self, mpropdef, args)
			return evaluate_expr(v, recv, f)
		else if mpropdef == mwritepropdef then
			assert args.length == 2
			var arg = args[1]
			if is_optional and arg.is_null then
				var f = v.new_frame(self, mpropdef, args)
				arg = evaluate_expr(v, recv, f)
			end
			v.write_attribute(attr, recv, arg)
			return null
		else
			abort
		end
	end

	# Evaluate and set the default value of the attribute in `recv`
	private fun init_expr(v: NaiveInterpreter, recv: Instance)
	do
		if is_lazy or is_optional then return
		if has_value then
			var f = v.new_frame(self, mreadpropdef.as(not null), [recv])
			evaluate_expr(v, recv, f)
			return
		end
		var mpropdef = self.mpropdef
		if mpropdef == null then return
		var mtype = self.mtype.as(not null)
		mtype = mtype.anchor_to(v.mainmodule, recv.mtype.as(MClassType))
		if mtype isa MNullableType then
			v.write_attribute(self.mpropdef.mproperty, recv, v.null_instance)
		end
	end

	private fun evaluate_expr(v: NaiveInterpreter, recv: Instance, f: Frame): Instance
	do
		assert recv isa MutableInstance
		v.frames.unshift(f)

		var val

		var nexpr = self.n_expr
		var nblock = self.n_block
		if nexpr != null then
			val = v.expr(nexpr)
		else if nblock != null then
			v.stmt(nblock)
			assert v.escapemark == return_mark
			val = v.escapevalue
			v.escapemark = null
		else
			abort
		end
		assert val != null

		v.frames.shift
		assert not v.is_escaping
		v.write_attribute(self.mpropdef.mproperty, recv, val)
		return val
	end
end

redef class AClassdef
	# Execute an implicit `mpropdef` associated with the current node.
	private fun call(v: NaiveInterpreter, mpropdef: MMethodDef, arguments: Array[Instance]): nullable Instance
	do
		if mpropdef.mproperty.is_root_init then
			assert arguments.length == 1
			if not mpropdef.is_intro then
				# standard call-next-method
				var superpd = mpropdef.lookup_next_definition(v.mainmodule, arguments.first.mtype)
				v.call(superpd, arguments)
			end
			return null
		else if mclassdef.default_init == mpropdef then
			var recv = arguments.first
			var initializers = mpropdef.initializers
			var no_init = false
			if not initializers.is_empty and not mpropdef.is_old_style_init then
				var i = 1
				for p in initializers do
					if p isa MMethod then
						var args = [recv]
						for x in p.intro.msignature.mparameters do
							args.add arguments[i]
							i += 1
						end
						v.send(p, args)
						if p.intro.is_calling_init then no_init = true
					else if p isa MAttribute then
						assert recv isa MutableInstance
						v.write_attribute(p, recv, arguments[i])
						i += 1
					else abort
				end
				assert i == arguments.length
			end
			if not no_init then v.send(mclass.the_root_init_mmethod.as(not null), [recv])
			return null
		else
			abort
		end
	end
end

redef class AExpr
	# Evaluate the node as a possible expression.
	# Return a possible value
	# NOTE: Do not call this method directly, but use `v.expr`
	# This method is here to be implemented by subclasses.
	protected fun expr(v: NaiveInterpreter): nullable Instance
	do
		fatal(v, "NOT YET IMPLEMENTED expr {class_name}")
		abort
	end

	# Evaluate the node as a statement.
	# NOTE: Do not call this method directly, but use `v.stmt`
	# This method is here to be implemented by subclasses (no need to return something).
	protected fun stmt(v: NaiveInterpreter)
	do
		expr(v)
	end

end

redef class ABlockExpr
	redef fun expr(v)
	do
		var last = self.n_expr.last
		for e in self.n_expr do
			if e == last then break
			v.stmt(e)
			if v.is_escaping then return null
		end
		return last.expr(v)
	end

	redef fun stmt(v)
	do
		for e in self.n_expr do
			v.stmt(e)
			if v.is_escaping then return
		end
	end
end

redef class AVardeclExpr
	redef fun expr(v)
	do
		var ne = self.n_expr
		if ne != null then
			var i = v.expr(ne)
			if i == null then return null
			v.write_variable(self.variable.as(not null), i)
			return i
		end
		return null
	end
end

redef class AVarExpr
	redef fun expr(v)
	do
		return v.read_variable(self.variable.as(not null))
	end
end

redef class AVarAssignExpr
	redef fun expr(v)
	do
		var i = v.expr(self.n_value)
		if i == null then return null
		v.write_variable(self.variable.as(not null), i)
		return i
	end
end

redef class AVarReassignExpr
	redef fun stmt(v)
	do
		var variable = self.variable.as(not null)
		var vari = v.read_variable(variable)
		var value = v.expr(self.n_value)
		if value == null then return
		var res = v.callsite(reassign_callsite, [vari, value])
		assert res != null
		v.write_variable(variable, res)
	end
end

redef class ASelfExpr
	redef fun expr(v)
	do
		return v.frame.arguments.first
	end
end

redef class AImplicitSelfExpr
	redef fun expr(v)
	do
		if not is_sys then return super
		return v.mainobj
	end
end

redef class AEscapeExpr
	redef fun stmt(v)
	do
		var ne = self.n_expr
		if ne != null then
			var i = v.expr(ne)
			if i == null then return
			v.escapevalue = i
		else
			v.escapevalue = null
		end
		v.escapemark = self.escapemark
	end
end

redef class AAbortExpr
	redef fun stmt(v)
	do
		fatal(v, "Aborted")
		exit(1)
	end
end

redef class AIfExpr
	redef fun expr(v)
	do
		var cond = v.expr(self.n_expr)
		if cond == null then return null
		if cond.is_true then
			return v.expr(self.n_then.as(not null))
		else
			return v.expr(self.n_else.as(not null))
		end
	end

	redef fun stmt(v)
	do
		var cond = v.expr(self.n_expr)
		if cond == null then return
		if cond.is_true then
			v.stmt(self.n_then)
		else
			v.stmt(self.n_else)
		end
	end
end

redef class AIfexprExpr
	redef fun expr(v)
	do
		var cond = v.expr(self.n_expr)
		if cond == null then return null
		if cond.is_true then
			return v.expr(self.n_then)
		else
			return v.expr(self.n_else)
		end
	end
end

redef class ADoExpr
	redef fun stmt(v)
	do
		# If this bloc has a catch, handle it with a do ... catch ... end
		if self.n_catch != null then
			var frame = v.frame
			v.catch_count += 1
			do
				v.stmt(self.n_block)
				v.is_escape(self.break_mark) # Clear the break (if any)
				v.catch_count -= 1
			catch
				# Restore the current frame if needed
				while v.frame != frame do v.frames.shift
				v.catch_count -= 1
				v.stmt(self.n_catch)
			end
		else
			v.stmt(self.n_block)
			v.is_escape(self.break_mark)
		end
	end
end

redef class AWhileExpr
	redef fun stmt(v)
	do
		loop
			var cond = v.expr(self.n_expr)
			if cond == null then return
			if not cond.is_true then return
			v.stmt(self.n_block)
			if v.is_escape(self.break_mark) then return
			v.is_escape(self.continue_mark) # Clear the break
			if v.is_escaping then return
		end
	end
end

redef class ALoopExpr
	redef fun stmt(v)
	do
		loop
			v.stmt(self.n_block)
			if v.is_escape(self.break_mark) then return
			v.is_escape(self.continue_mark) # Clear the break
			if v.is_escaping then return
		end
	end
end

redef class AForExpr
	redef fun stmt(v)
	do
		var iters = new Array[Instance]

		for g in n_groups do
			var col = v.expr(g.n_expr)
			if col == null then return
			if col.is_null then fatal(v, "Receiver is null")

			var iter = v.callsite(g.method_iterator, [col]).as(not null)
			iters.add iter
		end

		loop
			for g in n_groups, iter in iters do
				var isok = v.callsite(g.method_is_ok, [iter]).as(not null)
				if not isok.is_true then break label
				if g.variables.length == 1 then
					var item = v.callsite(g.method_item, [iter]).as(not null)
					#self.debug("item {item}")
					v.write_variable(g.variables.first, item)
				else if g.variables.length == 2 then
					var key = v.callsite(g.method_key, [iter]).as(not null)
					v.write_variable(g.variables[0], key)
					var item = v.callsite(g.method_item, [iter]).as(not null)
					v.write_variable(g.variables[1], item)
				else
					abort
				end
			end
			v.stmt(self.n_block)
			if v.is_escape(self.break_mark) then break
			v.is_escape(self.continue_mark) # Clear the break
			if v.is_escaping then break
			for g in n_groups, iter in iters do
				v.callsite(g.method_next, [iter])
			end
		end label
		for g in n_groups, iter in iters do
			var method_finish = g.method_finish
			if method_finish != null then
				v.callsite(method_finish, [iter])
			end
		end
	end
end

redef class AWithExpr
	redef fun stmt(v)
	do
		var expr = v.expr(self.n_expr)
		if expr == null then return

		v.callsite(method_start, [expr])
		v.stmt(self.n_block)
		v.is_escape(self.break_mark) # Clear the break

		# Execute the finally without an escape
		var old_mark = v.escapemark
		v.escapemark = null
		v.callsite(method_finish, [expr])
		# Restore the escape unless another escape was provided
		if v.escapemark == null then v.escapemark = old_mark
	end
end

redef class AAssertExpr
	redef fun stmt(v)
	do
		var cond = v.expr(self.n_expr)
		if cond == null then return
		if not cond.is_true then
			v.stmt(self.n_else)
			if v.is_escaping then return

			# Explain assert if it fails
			var explain_assert_str = explain_assert_str
			if explain_assert_str != null then
				var i = v.expr(explain_assert_str)
				if i isa MutableInstance then
					var res = v.send(v.force_get_primitive_method("to_cstring", i.mtype), [i])
					if res != null then
						var val = res.val
						if val != null then
							print_error "Runtime assert: {val.to_s}"
						end
					end
				end
			end

			var nid = self.n_id
			if nid != null then
				fatal(v, "Assert '{nid.text}' failed")
			else
				fatal(v, "Assert failed")
			end
			exit(1)
		end
	end
end

redef class AOrExpr
	redef fun expr(v)
	do
		var cond = v.expr(self.n_expr)
		if cond == null then return null
		if cond.is_true then return cond
		return v.expr(self.n_expr2)
	end
end

redef class AImpliesExpr
	redef fun expr(v)
	do
		var cond = v.expr(self.n_expr)
		if cond == null then return null
		if not cond.is_true then return v.true_instance
		return v.expr(self.n_expr2)
	end
end

redef class AAndExpr
	redef fun expr(v)
	do
		var cond = v.expr(self.n_expr)
		if cond == null then return null
		if not cond.is_true then return cond
		return v.expr(self.n_expr2)
	end
end

redef class ANotExpr
	redef fun expr(v)
	do
		var cond = v.expr(self.n_expr)
		if cond == null then return null
		return v.bool_instance(not cond.is_true)
	end
end

redef class AOrElseExpr
	redef fun expr(v)
	do
		var i = v.expr(self.n_expr)
		if i == null then return null
		if i != v.null_instance then return i
		return v.expr(self.n_expr2)
	end
end

redef class AIntegerExpr
	redef fun expr(v)
	do
		if value isa Int then return v.int_instance(value.as(Int))
		if value isa Byte then return v.byte_instance(value.as(Byte))
		if value isa Int8 then return v.int8_instance(value.as(Int8))
		if value isa Int16 then return v.int16_instance(value.as(Int16))
		if value isa UInt16 then return v.uint16_instance(value.as(UInt16))
		if value isa Int32 then return v.int32_instance(value.as(Int32))
		if value isa UInt32 then return v.uint32_instance(value.as(UInt32))
		return null
	end
end

redef class AFloatExpr
	redef fun expr(v)
	do
		return v.float_instance(self.value.as(not null))
	end
end

redef class ACharExpr
	redef fun expr(v)
	do
		if is_code_point then
			return v.int_instance(self.value.as(not null).code_point)
		end
		return v.char_instance(self.value.as(not null))
	end
end

redef class AArrayExpr
	redef fun expr(v)
	do
		var val = new Array[Instance]
		var old_comprehension = v.frame.comprehension
		v.frame.comprehension = val
		for nexpr in self.n_exprs do
			if nexpr isa AForExpr then
				v.stmt(nexpr)
			else
				var i = v.expr(nexpr)
				if i == null then return null
				val.add(i)
			end
		end
		v.frame.comprehension = old_comprehension
		var mtype = v.unanchor_type(self.mtype.as(not null)).as(MClassType)
		var elttype = mtype.arguments.first
		return v.array_instance(val, elttype)
	end
end

redef class AugmentedStringFormExpr
	# Factorize the making of a `Regex` object from a literal prefixed string
	fun make_re(v: NaiveInterpreter, rs: Instance): nullable Instance do
		var tore = to_re
		assert tore != null
		var res = v.callsite(tore, [rs])
		if res == null then
			print "Cannot call property `to_re` on {self}"
			abort
		end
		for j in suffix.chars do
			if j == 'i' then
				var prop = ignore_case
				assert prop != null
				v.callsite(prop, [res, v.bool_instance(true)])
				continue
			end
			if j == 'm' then
				var prop = newline
				assert prop != null
				v.callsite(prop, [res, v.bool_instance(true)])
				continue
			end
			if j == 'b' then
				var prop = extended
				assert prop != null
				v.callsite(prop, [res, v.bool_instance(false)])
				continue
			end
			# Should not happen, this needs to be updated
			# along with the addition of new suffixes
			abort
		end
		return res
	end
end

redef class AStringFormExpr
	redef fun expr(v) do return v.string_instance(value)
end

redef class AStringExpr
	redef fun expr(v) do
		var s = v.string_instance(value)
		if is_string then return s
		if is_bytestring then
			var ns = v.c_string_instance_from_ns(bytes.items, bytes.length)
			var ln = v.int_instance(bytes.length)
			var prop = to_bytes_with_copy
			assert prop != null
			var res = v.callsite(prop, [ns, ln])
			if res == null then
				print "Cannot call property `to_bytes` on {self}"
				abort
			end
			s = res
		else if is_re then
			var res = make_re(v, s)
			assert res != null
			s = res
		else
			print "Unimplemented prefix or suffix for {self}"
			abort
		end
		return s
	end
end

redef class ASuperstringExpr
	redef fun expr(v)
	do
		var array = new Array[Instance]
		for nexpr in n_exprs do
			var i = v.expr(nexpr)
			if i == null then return null
			array.add(i)
		end
		var i = v.array_instance(array, v.mainmodule.object_type)
		var res = v.send(v.force_get_primitive_method("plain_to_s", i.mtype), [i])
		assert res != null
		if is_re then res = make_re(v, res)
		return res
	end
end

redef class ACrangeExpr
	redef fun expr(v)
	do
		var e1 = v.expr(self.n_expr)
		if e1 == null then return null
		var e2 = v.expr(self.n_expr2)
		if e2 == null then return null
		var mtype = v.unanchor_type(self.mtype.as(not null))
		var res = new MutableInstance(mtype)
		v.init_instance(res)
		v.callsite(init_callsite, [res, e1, e2])
		return res
	end
end

redef class AOrangeExpr
	redef fun expr(v)
	do
		var e1 = v.expr(self.n_expr)
		if e1 == null then return null
		var e2 = v.expr(self.n_expr2)
		if e2 == null then return null
		var mtype = v.unanchor_type(self.mtype.as(not null))
		var res = new MutableInstance(mtype)
		v.init_instance(res)
		v.callsite(init_callsite, [res, e1, e2])
		return res
	end
end

redef class ATrueExpr
	redef fun expr(v)
	do
		return v.bool_instance(true)
	end
end

redef class AFalseExpr
	redef fun expr(v)
	do
		return v.bool_instance(false)
	end
end

redef class ANullExpr
	redef fun expr(v)
	do
		return v.null_instance
	end
end

redef class AIsaExpr
	redef fun expr(v)
	do
		var i = v.expr(self.n_expr)
		if i == null then return null
		var mtype = v.unanchor_type(self.cast_type.as(not null))
		return v.bool_instance(v.is_subtype(i.mtype, mtype))
	end
end

redef class AAsCastExpr
	redef fun expr(v)
	do
		var i = v.expr(self.n_expr)
		if i == null then return null
		var mtype = self.mtype.as(not null)
		var amtype = v.unanchor_type(mtype)
		if not v.is_subtype(i.mtype, amtype) then
			fatal(v, "Cast failed. Expected `{amtype}`, got `{i.mtype}`")
		end
		return i
	end
end

redef class AAsNotnullExpr
	redef fun expr(v)
	do
		var i = v.expr(self.n_expr)
		if i == null then return null
		if i.is_null then
			fatal(v, "Cast failed")
		end
		return i
	end
end

redef class AParExpr
	redef fun expr(v)
	do
		return v.expr(self.n_expr)
	end
end

redef class AOnceExpr
	redef fun expr(v)
	do
		if v.onces.has_key(self) then
			return v.onces[self]
		else
			var res = v.expr(self.n_expr)
			if res == null then return null
			v.onces[self] = res
			return res
		end
	end
end

redef class ASendExpr
	redef fun expr(v)
	do
		var recv = v.expr(self.n_expr)
		if recv == null then return null

		# Safe call shortcut if recv is null
		if is_safe and recv.is_null then
			return recv
		end

		var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
		if args == null then return null
		var res = v.callsite(callsite, args)
		return res
	end
end

redef class ACallrefExpr
	redef fun expr(v)
	do
		var recv = v.expr(self.n_expr)
		if recv == null then return null
		var mtype = self.mtype
		assert mtype != null
		# In case we are in generic class where formal parameter can not
		# be resolved.
		var mtype2 = v.unanchor_type(mtype)
		var inst = new CallrefInstance(mtype2, recv, callsite.as(not null))
		return inst
	end
end

redef class ASendReassignFormExpr
	redef fun stmt(v)
	do
		var recv = v.expr(self.n_expr)
		if recv == null then return
		var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
		if args == null then return
		var value = v.expr(self.n_value)
		if value == null then return

		var read = v.callsite(callsite, args)
		assert read != null

		var write = v.callsite(reassign_callsite, [read, value])
		assert write != null

		args.add(write)

		v.callsite(write_callsite, args)
	end
end

redef class ASuperExpr
	redef fun expr(v)
	do
		var recv = v.frame.arguments.first

		var callsite = self.callsite
		if callsite != null then
			var args
			if self.n_args.n_exprs.is_empty then
				# Add automatic arguments for the super init call
				args = [recv]
				for i in [0..callsite.msignature.arity[ do
					args.add(v.frame.arguments[i+1])
				end
			else
				args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
				if args == null then return null
			end

			# Super init call
			var res = v.callsite(callsite, args)
			return res
		end

		# Standard call-next-method
		var mpropdef = self.mpropdef
		mpropdef = mpropdef.lookup_next_definition(v.mainmodule, recv.mtype)

		var args
		if self.n_args.n_exprs.is_empty then
			args = v.frame.arguments
		else
			args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
			if args == null then return null
		end

		var res = v.call(mpropdef, args)
		return res
	end
end

redef class ANewExpr
	redef fun expr(v)
	do
		var mtype = v.unanchor_type(self.recvtype.as(not null))
		var recv: Instance = new MutableInstance(mtype)
		v.init_instance(recv)
		var callsite = self.callsite
		if callsite == null then return recv

		var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
		if args == null then return null
		var res2 = v.callsite(callsite, args)
		if res2 != null then
			#self.debug("got {res2} from {mproperty}. drop {recv}")
			return res2
		end
		return recv
	end
end

redef class AAttrExpr
	redef fun expr(v)
	do
		var recv = v.expr(self.n_expr)
		if recv == null then return null
		if recv.is_null then fatal(v, "Receiver is null")
		var mproperty = self.mproperty.as(not null)
		return v.read_attribute(mproperty, recv)
	end
end

redef class AAttrAssignExpr
	redef fun stmt(v)
	do
		var recv = v.expr(self.n_expr)
		if recv == null then return
		if recv.is_null then fatal(v, "Receiver is null")
		var i = v.expr(self.n_value)
		if i == null then return
		var mproperty = self.mproperty.as(not null)
		v.write_attribute(mproperty, recv, i)
	end
end

redef class AAttrReassignExpr
	redef fun stmt(v)
	do
		var recv = v.expr(self.n_expr)
		if recv == null then return
		if recv.is_null then fatal(v, "Receiver is null")
		var value = v.expr(self.n_value)
		if value == null then return
		var mproperty = self.mproperty.as(not null)
		var attr = v.read_attribute(mproperty, recv)
		var res = v.callsite(reassign_callsite, [attr, value])
		assert res != null
		v.write_attribute(mproperty, recv, res)
	end
end

redef class AIssetAttrExpr
	redef fun expr(v)
	do
		var recv = v.expr(self.n_expr)
		if recv == null then return null
		if recv.is_null then fatal(v, "Receiver is null")
		var mproperty = self.mproperty.as(not null)
		return v.bool_instance(v.isset_attribute(mproperty, recv))
	end
end

redef class AVarargExpr
	redef fun expr(v)
	do
		return v.expr(self.n_expr)
	end
end

redef class ASafeExpr
	redef fun expr(v)
	do
		return v.expr(self.n_expr)
	end
end

redef class ANamedargExpr
	redef fun expr(v)
	do
		return v.expr(self.n_expr)
	end
end

redef class ADebugTypeExpr
	redef fun stmt(v)
	do
		# do nothing
	end
end
src/interpreter/naive_interpreter.nit:17,1--2471,3