--/**
-- * <copyright>
-- *
-- * Copyright (c) 2005, 2009 IBM Corporation and others.
-- * All rights reserved.   This program and the accompanying materials
-- * are made available under the terms of the Eclipse Public License v2.0
-- * which accompanies this distribution, and is available at
-- * http://www.eclipse.org/legal/epl-v20.html
-- *
-- * Contributors:
-- *   See (or edit) Notice Declaration below
-- *
-- * </copyright>
-- */
--
-- The Essential OCL Lexer
--

%options escape=$
%options la=2
%options fp=EssentialOCLLexer,prefix=Char_
%options single-productions
%options noserialize
%options package=org.eclipse.ocl.parser
%options template=../lpg/LexerTemplateF.gi
%options filter=EssentialOCLKWLexer.gi
%options export_terminals=("EssentialOCLParsersym.java", "TK_")
%options include_directory="../lpg"

%Import
	LexerBasicMapF.gi
%End

%Define

	--
	-- Definition of macros used in the template
	--
	$action_class /.$file_prefix./  -- Deprecated.
	$eof_token /.$_EOF_TOKEN./
    $environment_class /.Environment<?,?,?,?,?,?,?,?,?,?,?,?>./
    $adapt_environment /.OCLUtil.getAdapter(environment, BasicEnvironment.class)./
    $environment_import /.org.eclipse.ocl.Environment./

 	--
	-- Redefinition of macros used in the template
	-- NB: They are also used in the included file LexerBasicMapF.g
	--
 	$prs_stream_class /.DerivedPrsStream./
 	$lex_stream_class /.DerivedLexStream./

	--
	-- Definition of macro used in the included file LexerBasicMapF.g
	--
	$kw_lexer_class /.EssentialOCLKWLexer./
	$copyright_contributions /.*./

%End

%Headers
	/.

    // Some OCL additions to make lexer work with an input reader
	/**
	 * @since 3.0
	 */
	public $action_type($environment_class environment, Reader reader, String filename) throws java.io.IOException {
		super($adapt_environment);
		oclEnvironment = environment;
		reset(reader, filename);
	}

   // OCL addition to reset the lexer stream from an input reader
	/**
	 * @since 3.0
	 */
    @Override
    public void reset(Reader reader, String filename) throws java.io.IOException {
    	char[] input_chars = getInputChars(reader);
        reset(input_chars, filename, ECLIPSE_TAB_VALUE);
    }
	./
%End
%Notice
	/./**
 * Essential OCL Lexer
 * <copyright>
 *
 * Copyright (c) 2005, 2010 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * Contributors:
 *   IBM - Initial API and implementation
 *   E.D.Willink - Lexer and Parser refactoring to support extensibility and flexible error handling
 *   Borland - Bug 242880
 *   E.D.Willink - Bug 292112, 295166
 *   Adolfo Sanchez-Barbudo Herrera (Open Canarias) - LPG v 2.0.17 adoption (242153)
 *   Adolfo Sanchez-Barbudo Herrera (Open Canarias) - Introducing new LPG templates (299396)
 $copyright_contributions
 *******************************************************************************/
	./
%End

%Globals
    /.
    import java.io.Reader;

    import $environment_import;
    import org.eclipse.ocl.lpg.BasicEnvironment;
    import org.eclipse.ocl.lpg.DerivedPrsStream;
    import org.eclipse.ocl.lpg.DerivedLexStream;
    import org.eclipse.ocl.util.OCLUtil;
    ./
%End

%Export

	IDENTIFIER
	QUOTED_IDENTIFIER
	INTEGER_LITERAL
	REAL_LITERAL
	STRING_LITERAL

	PLUS
	MINUS
	MULTIPLY
	DIVIDE

	GREATER
	LESS
	EQUAL
	GREATER_EQUAL
	LESS_EQUAL
	NOT_EQUAL

	LPAREN
	RPAREN
	LBRACE
	RBRACE
	LBRACKET
	RBRACKET

	ARROW
	BAR
	COMMA
	COLON
	COLONCOLON
	SEMICOLON
	DOT
	DOTDOT

	SINGLE_LINE_COMMENT
	MULTI_LINE_COMMENT

%End

%Terminals
	CtlCharNotWS

	LF   CR   HT   FF

	a b c d e f g h i j k l m n o p q r s t u v w x y z
	_

	A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

	0 1 2 3 4 5 6 7 8 9

	AfterASCIINotAcute
	Space        ::= ' '
	LF           ::= NewLine
	CR           ::= Return
	HT           ::= HorizontalTab
	FF           ::= FormFeed
	DoubleQuote  ::= '"'
	SingleQuote  ::= "'"
	Percent      ::= '%'
	VerticalBar  ::= '|'
	Exclamation  ::= '!'
	AtSign       ::= '@'
	BackQuote    ::= '`'
	Acute        ::= 'Ã¯Â¿Â½'
	Tilde        ::= '~'
	Sharp        ::= '#'
	DollarSign   ::= '$'
	Ampersand    ::= '&'
	Caret        ::= '^'
	Colon        ::= ':'
	SemiColon    ::= ';'
	BackSlash    ::= '\'
	LeftBrace    ::= '{'
	RightBrace   ::= '}'
	LeftBracket  ::= '['
	RightBracket ::= ']'
	QuestionMark ::= '?'
	Comma        ::= ','
	Dot          ::= '.'
	LessThan     ::= '<'
	GreaterThan  ::= '>'
	Plus         ::= '+'
	Minus        ::= '-'
	Slash        ::= '/'
	Star         ::= '*'
	LeftParen    ::= '('
	RightParen   ::= ')'
	Equal        ::= '='

%End

%Start
	Token
%End

%Rules

	---------------------  Rules for Scanned Tokens --------------------------------
	-- The lexer creates an array list of tokens which is defined in the PrsStream class.
	-- A token has three attributes: a start offset, an end offset and a kind.
	--
	-- Only rules that produce complete tokens have actions to create token objects.
	-- When making a token, calls to the methods, $getToken(1) and $getRightSpan(),
	-- provide the offsets (i.e. the span) of a rule's right hand side (rhs) and thus of the token.
	-- For a rule of the form A ::= A1 A2 ... An, the start offset of the rhs of A is given by
	-- $getToken(1) or by $getLeftSpan() and the end offset by $getRightSpan().
	--
	-- Regarding rules for parsing in general, note that for a rhs symbol Ai, the
	-- method $getToken(i) returns the location of the leftmost character derived from Ai.
	-- The method $getLeftSpan(i) returns the same location unless Ai produces %empty in which case
	-- it returns the location of the last character derived before reducing Ai to %empty.
	-- The method $getRightSpan(i) returns the location of the rightmost character derived from Ai
	-- unless Ai produces %empty in which case it returns the location of the last character
	-- derived before reducing Ai to %empty.
	--------------------------------------------------------------------------------
	Token ::= Identifier
		/.$BeginAction
					checkForKeyWord();
		  $EndAction
		./

	-- Deprecated
	Token ::= '"' SLNotDQ '"'
		/.$BeginAction
					makeToken($_IDENTIFIER);
		  $EndAction
		./

	Token ::= '_' SingleQuote SLNotSQOpt SingleQuote
		/.$BeginAction
					makeToken($_QUOTED_IDENTIFIER);
		  $EndAction
		./

	Token ::= SingleQuote SLNotSQOpt SingleQuote
		/.$BeginAction
					makeToken($_STRING_LITERAL);
		  $EndAction
		./

	-- Deprecated
	Token ::= Acute SLNotSQOpt Acute
		/.$BeginAction
					makeToken($_STRING_LITERAL);
		  $EndAction
		./

	-- Deprecated
	Token ::= BackQuote SLNotSQOpt Acute
		/.$BeginAction
					makeToken($_STRING_LITERAL);
		  $EndAction
		./

	Token ::= IntegerLiteral
		/.$NoAction
		./

	Token ::= IntegerLiteral DotToken
		/.$NoAction
		./

	Token ::= IntegerLiteral DotDotToken
		/.$NoAction
		./

	Token ::= RealLiteral
		/.$BeginAction
					makeToken($_REAL_LITERAL);
		  $EndAction
		./

	Token ::= SLC
		/.$BeginAction
					makeComment($_SINGLE_LINE_COMMENT);
		  $EndAction
		./

    Token ::= '/' '*' Inside Stars '/'
        /.$BeginAction
                    makeComment($_MULTI_LINE_COMMENT);
          $EndAction
        ./

	Token ::= WS -- White Space is scanned but not added to output vector
		/.$BeginAction
					skipToken();
		  $EndAction
		./

	Token ::= '+'
		/.$BeginAction
					makeToken($_PLUS);
		  $EndAction
		./

	Token ::= '-'
		/.$BeginAction
					makeToken($_MINUS);
		  $EndAction
		./

	Token ::= '*'
		/.$BeginAction
					makeToken($_MULTIPLY);
		  $EndAction
		./

	Token ::= '/'
		/.$BeginAction
					makeToken($_DIVIDE);
		  $EndAction
		./

	Token ::= '('
		/.$BeginAction
					makeToken($_LPAREN);
		  $EndAction
		./

	Token ::= ')'
		/.$BeginAction
					makeToken($_RPAREN);
		  $EndAction
		./

	Token ::= '>'
		/.$BeginAction
					makeToken($_GREATER);
		  $EndAction
		./

	Token ::= '<'
		/.$BeginAction
					makeToken($_LESS);
		  $EndAction
		./

	Token ::= '='
		/.$BeginAction
					makeToken($_EQUAL);
		  $EndAction
		./

	Token ::= '>' '='
		/.$BeginAction
					makeToken($_GREATER_EQUAL);
		  $EndAction
		./

	Token ::= '<' '='
		/.$BeginAction
					makeToken($_LESS_EQUAL);
		  $EndAction
		./

	Token ::= '<' '>'
		/.$BeginAction
					makeToken($_NOT_EQUAL);
		  $EndAction
		./

	Token ::= '['
		/.$BeginAction
					makeToken($_LBRACKET);
		  $EndAction
		./

	Token ::= ']'
		/.$BeginAction
					makeToken($_RBRACKET);
		  $EndAction
		./

	Token ::= '{'
		/.$BeginAction
					makeToken($_LBRACE);
		  $EndAction
		./

	Token ::= '}'
		/.$BeginAction
					makeToken($_RBRACE);
		  $EndAction
		./

	Token ::= '-' '>'
		/.$BeginAction
					makeToken($_ARROW);
		  $EndAction
		./

	Token ::= '|'
		/.$BeginAction
					makeToken($_BAR);
		  $EndAction
		./

	Token ::= ','
		/.$BeginAction
					makeToken($_COMMA);
		  $EndAction
		./

	Token ::= ':'
		/.$BeginAction
					makeToken($_COLON);
		  $EndAction
		./

	Token ::= ':' ':'
		/.$BeginAction
					makeToken($_COLONCOLON);
		  $EndAction
		./

	Token ::= ';'
		/.$BeginAction
					makeToken($_SEMICOLON);
		  $EndAction
		./

	Token ::= DotToken
		/.$NoAction
		./

	DotToken ::= '.'
		/.$BeginAction
					makeToken($_DOT);
		  $EndAction
		./

	Token ::= DotDotToken
		/.$NoAction
		./

	DotDotToken ::= '.' '.'
		/.$BeginAction
					makeToken($_DOTDOT);
		  $EndAction
		./

    IntegerLiteral ::= Integer
		/.$BeginAction
					makeToken($_INTEGER_LITERAL);
		  $EndAction
		./

    RealLiteral -> Decimal
                 | Decimal Exponent
                 | Integer Exponent

    Inside ::= Inside Stars NotSlashOrStar
             | Inside '/'
             | Inside NotSlashOrStar
             | %empty

    Stars -> '*'
           | Stars '*'

    SLC -> '-' '-'
         | SLC NotEol

    Integer -> Digit
             | Integer Digit

    Decimal -> Integer '.' Integer

    Exponent -> LetterEe Integer
              | LetterEe '-' Integer
              | LetterEe '+' Integer

    WSChar -> Space
            | LF
            | CR
            | HT
            | FF

    Letter -> LowerCaseLetter
            | UpperCaseLetter
            | _
            | AfterASCIINotAcute

    LowerCaseLetter -> a | b | c | d | e | f | g | h | i | j | k | l | m |
                       n | o | p | q | r | s | t | u | v | w | x | y | z

    UpperCaseLetter -> A | B | C | D | E | F | G | H | I | J | K | L | M |
                       N | O | P | Q | R | S | T | U | V | W | X | Y | Z

    Digit -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

    LetterEe -> 'E'
              | 'e'

    WS -> WSChar
        | WS WSChar

    Identifier -> Letter
                | Identifier Letter
                | Identifier Digit
                | Identifier DollarSign

    SpecialNotStar -> '+' | '-' | '/' | '(' | ')' | '"' | '!' | '@' | '`' | '~' |
                      '%' | '&' | '^' | ':' | ';' | "'" | '\' | '|' | '{' | '}' |
                      '[' | ']' | '?' | ',' | '.' | '<' | '>' | '=' | '#' | DollarSign

    SpecialNotSlash -> '+' | '-' | -- exclude the star as well
                       '(' | ')' | '"' | '!' | '@' | '`' | '~' |
                       '%' | '&' | '^' | ':' | ';' | "'" | '\' | '|' | '{' | '}' |
                       '[' | ']' | '?' | ',' | '.' | '<' | '>' | '=' | '#' | DollarSign

    SpecialNotSQNotDQ -> '+' | '-' | '/' | '(' | ')' | '*' | '!' | '@' | '`' | '~' |
                         '%' | '&' | '^' | ':' | ';' | '|' | '{' | '}' |
                         '[' | ']' | '?' | ',' | '.' | '<' | '>' | '=' | '#' | DollarSign

    SpecialNotDQ -> SpecialNotSQNotDQ | "'"
    SpecialNotSQ -> SpecialNotSQNotDQ | '"'

    EscapedSymbols -> NotSQNotDQ | '"' | "'" | '\'
    BackslashEscapedSymbol -> '\' EscapedSymbols

    NotSlashOrStar -> Letter
                    | Digit
                    | SpecialNotSlash
                    | WSChar

    NotEol -> Letter
            | Digit
            | Space
            | '*'
            | SpecialNotStar
            | HT
            | FF
            | CtlCharNotWS

    NotSQNotDQ -> Letter
           | Digit
           | SpecialNotSQNotDQ
           | Space

    NotDQ -> Letter
           | Digit
           | SpecialNotDQ
           | Space
           | BackslashEscapedSymbol

    NotSQ -> Letter
           | Digit
           | SpecialNotSQ
           | Space
           | BackslashEscapedSymbol

	SLNotDQ -> NotDQ
	         | SLNotDQ NotDQ

	SLNotSQ -> NotSQ
	         | SLNotSQ NotSQ

	SLNotSQOpt -> %empty
	            | SLNotSQ

%End


--/**
-- * <copyright>
-- *
-- * Copyright (c) 2010, 2009 IBM Corporation and others.
-- * All rights reserved.   This program and the accompanying materials
-- * are made available under the terms of the Eclipse Public License v2.0
-- * which accompanies this distribution, and is available at
-- * http://www.eclipse.org/legal/epl-v20.html
-- *
-- * Contributors:
-- *   See (or edit) Notice Declaration below
-- *
-- * </copyright>
-- */
--
-- The Complete OCL KeyWord Lexer
--

%options slr
%options fp=OCLKWLexer,prefix=Char_
%options noserialize
%options package=org.eclipse.ocl.xtext.essentialocl.parser
%options template=../lpg/KeywordTemplateF.gi
%options export_terminals=("OCLParsersym.java", "TK_")
%options include_directory="../lpg"

%Import
	EssentialOCLKWLexer.gi
%End

%Notice
	/./**
 * Complete OCL Keyword Lexer
 * <copyright>
 *
 * Copyright (c) 2010, 2009 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * Contributors:
 *   IBM - Initial API and implementation
 *   E.D.Willink - Bug 292112
 *   Adolfo Sanchez-Barbudo Herrera (Open Canarias) - LPG v 2.0.17 adoption (242153)
 *   Adolfo Sanchez-Barbudo Herrera (Open Canarias) - Introducing new LPG templates (299396)
$copyright_contributions
 *******************************************************************************/
	./
%End

%Export
	inv
	pre
	post
	context
	package
	endpackage
	def
	body
	derive
	init
	--
	-- the following were introduced in the OCL 2.1 RTF 09-05-02.
	--
	static

	OclMessage
%End

%Rules

-- The Goal for the parser is a single Keyword

	KeyWord ::=
		i n v
		/.$BeginAction
			$setResult($_inv);
		  $EndAction
		./

		| p r e
		/.$BeginAction
			$setResult($_pre);
		  $EndAction
		./

		| p o s t
		/.$BeginAction
			$setResult($_post);
		  $EndAction
		./

		| b o d y
		/.$BeginAction
			$setResult($_body);
		  $EndAction
		./

		| c o n t e x t
		/.$BeginAction
			$setResult($_context);
		  $EndAction
		./

		| p a c k a g e
		/.$BeginAction
			$setResult($_package);
		  $EndAction
		./

		| e n d p a c k a g e
		/.$BeginAction
			$setResult($_endpackage);
		  $EndAction
		./

		| d e f
		/.$BeginAction
			$setResult($_def);
		  $EndAction
		./

		| d e r i v e
		/.$BeginAction
			$setResult($_derive);
		  $EndAction
		./

		| i n i t
		/.$BeginAction
			$setResult($_init);
		  $EndAction
		./

		| O c l M e s s a g e
		/.$BeginAction
			$setResult($_OclMessage);
		  $EndAction
		./

		| s t a t i c
		/.$BeginAction
			$setResult($_static);
		  $EndAction
		./
%End


MitM_CookieCount := 0;
MitM_CookieJar := rec();

InstallGlobalFunction(MitM_CompleteProcedure,
function(result, attr...)
    if Length(attr) = 0 then
        attr := rec();
    elif Length(attr) = 1 then
        attr := attr[1];
    else
        ErrorNoReturn("MitM_CompleteProcedure: takes 1 or 2 arguments (not ",
                      Length(attr) + 1, ")");
    fi;
    return MitM_OMRecToXML(MitM_OMRecToOMOBJRec(
                 OMATTR( attr
                       , OMA( OMS( "scscp1"
                                 , "procedure_completed" )
                            , result ) ) ) );
end);

InstallGlobalFunction(MitM_TerminateProcedure,
function(message, attr...)
    if Length(attr) = 0 then
        attr := rec();
    elif Length(attr) = 1 then
        attr := attr[1];
    else
        ErrorNoReturn("MitM_TerminateProcedure: takes 1 or 2 arguments (not ",
                      Length(attr) + 1, ")");
    fi;
    return MitM_OMRecToXML(MitM_OMRecToOMOBJRec(
                 OMATTR( attr
                       , OMA( OMS( "scscp1"
                                 , "procedure_terminated" )
                            , OME( OMS( "scscp1"
                                      , "error_system_specific" )
                                 , [ OMSTR(message) ] ) ) ) ) );
end);

InstallValue(MitM_SCSCPHandlers, rec(
    procedure_call := function(attr, oma)
        local t, eval, rattr, res;

        Info(InfoMitMServer, 15, " Evaluating... ", oma);
        Info(InfoMitMServer, 15, " Attributes... ", attr);

        t := NanosecondsSinceEpoch();
        eval := MitM_OMRecToGAP(oma);
        t := NanosecondsSinceEpoch() - t;

        if eval.success then
            rattr := rec( call_id := attr.call_id
                        , info_runtime := t / 1000000. );
            if IsBound(attr.option_return_cookie) then
                MitM_CookieJar.(MitM_CookieCount) := eval.result;
                res := MitM_GAPToOMRec(MitM_CookieCount);
                MitM_CookieCount := MitM_CookieCount + 1;
            elif IsBound(attr.option_return_object) then
                res := MitM_GAPToOMRec(eval.result);
            elif IsBound(attr.option_return_nothing) then
                res := rec();
            fi;

            return MitM_CompleteProcedure(res, rattr);
        else
            Info(InfoMitMServer, 15, " Error during evaluation: ", eval.error);
            return MitM_TerminateProcedure(eval.error, rec(call_id := attr.call_id));
        fi;
    end
) );

InstallGlobalFunction(MitM_HandleSCSCP,
function(node)
    local attr, scscp_call, scscp_oma, content;

    content := MitM_Content(node);
    # Validate wrt SCSCP v1.3 spec - procedure call (4.1.1)
    if not (Length(content) = 1 and MitM_Tag(content[1]) = "OMATTR") then
        Info(InfoMitMServer, 15, " Invalid procedure call: OMATTR expected");
        return MitM_TerminateProcedure("procedure call: OMOBJ should contain one OMATTR and nothing else");
    elif MitM_Tag(MitM_Content(content[1])[2]) <> "OMA" then
        Info(InfoMitMServer, 15, " Invalid procedure call: OMA expected");
        return MitM_TerminateProcedure("procedure call: OMOBJ: OMATTR's 2nd object should be an OMA");
    elif not (Length(MitM_Content(MitM_Content(content[1])[2])) = 2 and
              MitM_Tag(MitM_Content(MitM_Content(content[1])[2])[1]) = "OMS") then
        Info(InfoMitMServer, 15, " Invalid procedure call: OMS expected");
        return MitM_TerminateProcedure("procedure call: OMOBJ: OMATTR: OMA's 1st object should be an OMS");
    elif MitM_Tag(MitM_Content(MitM_Content(content[1])[2])[2]) <> "OMA" then
        Info(InfoMitMServer, 15, " Invalid procedure call: OMA expected");
        return MitM_TerminateProcedure("procedure call: OMOBJ: OMATTR: OMA's 2nd object should be an OMA");
    fi;

    attr := MitM_ATPToRec(MitM_Content(content[1])[1]);
    scscp_call := MitM_Content(MitM_Content(content[1])[2])[1];
    scscp_oma := MitM_Content(MitM_Content(content[1])[2])[2];

    if MitM_CD(scscp_call) = "scscp1" then
        if MitM_Name(scscp_call) = "procedure_call" then
            return MitM_SCSCPHandlers.procedure_call(attr, scscp_oma);
        elif MitM_Name(scscp_call) = "terminate_procedure" then
            # TODO:
        fi;
    else
        Info(InfoMitMServer, 15, " Unsupported CD ", scscp_call.attributes.cd);
        return fail;
    fi;
end);

InstallGlobalFunction(MitM_SCSCPHandler,
function(addr, stream)
    local done, version, r, reply, obj;
    done := false;

    Info(InfoMitMServer, 5, "Accepted MitM Connection on ", TCP_AddrToString(addr));
    version := MitM_SCSCPServerHandshake(stream, stream);
    Info(InfoMitMServer, 5, " SCSCP Protocol Version ", version);
    while not done do
        r := MitM_ReadSCSCP(stream);
        Info(InfoMitMServer, 15, " Received: ", r);
        if r.success <> true then
            Info(InfoMitMServer, 15, " Bad object received");
            Info(InfoMitMServer, 15, "  error: ", r.error);
            Info(InfoMitMServer, 15, " closing connection.");
            CloseStream(stream);
            done := true;
            # WriteLine(stream, Concatenation("error: ", r.error));
        else
            reply := MitM_HandleSCSCP(r.result);
            Info(InfoMitMServer, 15, " Evaluated to ", reply);
            WriteLine(stream, "<?scscp start ?>");
            WriteLine(stream, reply);
            WriteLine(stream, "<?scscp end ?>");
        fi;
    od;
    Info(InfoMitMServer, 5, "Leaving handler for ", TCP_AddrToString(addr));
end);

InstallGlobalFunction(StartMitMServer,
function(args...)
    local opt;

    opt := ShallowCopy(MitM_DefaultServerOptions);
    if IsBound(args[1]) and IsString(args[1]) then
        opt.hostname := args[1];
    fi;
    if IsBound(args[2]) and IsPosInt(args[2]) then
        opt.port := args[2];
    fi;

    Info(InfoMitMServer, 5, "Starting MitM TCP Server on ", opt.hostname, ":", opt.port);
    StartTCPServer(opt.hostname, opt.port, MitM_SCSCPHandler);
end);

InstallGlobalFunction(StreamToMitMServer,
function(hostname, port...)
    local stream;
    if Length(port) = 0 then
        port := 26133; # SCSCP default
    elif Length(port) = 1 then
        port := port[1];
    else
        Error("MitM_ConnectionToServer: 1 or 2 arguments expected, but ",
              Length(port) + 1, " found");
    fi;
    stream := ConnectInputOutputTCPStream(hostname, port);
    MitM_SCSCPClientHandshake(stream, stream);
    return stream;
end);

InstallGlobalFunction(SendObjToMitMServer,
function(stream, obj)
    local xml;
    xml := MitM_OMRecToXML(obj);
    WriteLine(stream, "<?scscp start ?>");
    WriteLine(stream, xml);
    WriteLine(stream, "<?scscp end ?>");
    return MitM_ReadSCSCP(stream);
end);

InstallGlobalFunction(MitM_ProcedureCall,
function(obj)
    local base64, call_id, opts, call, attr;
    # obj should be an OMA to be run
    base64 := Concatenation("ABCDEFGHIJKLMNOPQRSTUVWXYZ",
                            "abcdefghijklmnopqrstuvwxyz",
                            "0123456789+/");
    call_id := Concatenation(MitM_cdbase, ":",
                             List([1..16], i -> Random(base64)));
    # TODO: support option_return_cookie and option_return_nothing
    opts := rec(call_id := OMSTR(call_id), option_return_object := OMSTR(""));
    call := OMA(OMS("scscp1", "procedure_call"), obj);
    attr := OMATTR(opts, call);
    return OMOBJ(attr);
end);

InstallGlobalFunction(GetAllowedHeads,
function(stream)
    local call, out, obj, attr, oma_outer, oma_inner, list;
    call := MitM_ProcedureCall(OMA(OMS("scscp2", "get_allowed_heads")));
    out := SendObjToMitMServer(stream, call);
    if not out.success then
        return fail;
    fi;
    obj := out.result;
    # TODO: check that the server successfully sent a list of heads
    attr := MitM_Content(obj)[1];
    oma_outer := MitM_Content(attr)[2];
    oma_inner := MitM_Content(oma_outer)[2];
    list := MitM_Content(oma_inner);
    return list{[2..Length(list)]};
end);


InstallGlobalFunction(TCP_AddrToString,
addr -> JoinStringsWithSeparator(List(addr{[5..8]},
                                      x -> String(INT_CHAR(x))), "."));

InstallGlobalFunction( StartTCPServer,
function(hostname, port, handlerCallback)
    local socket, addr, client, stream;
    socket := ListeningTCPSocket(hostname, port);
    while true do
        # Currently we accept connections from anyone.
        addr := IO_MakeIPAddressPort( "0.0.0.0", 0 );

        # Accept connection, and open stream.
        client := IO_accept(socket, addr);

        # TODO: prettier
        Info(InfoTCPSockets, 5, "Accepted connection from: ",
             TCP_AddrToString(addr));
        stream := AcceptInputOutputTCPStream(client);

        # Handle connection
        handlerCallback(addr, stream);
    od;
end);

InstallGlobalFunction( ListeningTCPSocket,
function(hostname, port)
    local socket, client, desc, stream, res, bindaddr, listenname;

    res := IO_gethostbyname(hostname);
    if res = fail then
        ErrorNoReturn("ListeningTCPSocket: lookup failed on address ",
                      hostname);
    fi;
    bindaddr := res.addr[1];
    listenname := res.name;

    if not IsPosInt(port) or (port > 65535) then
        ErrorNoReturn("ListeningTCPSocket:\n<port> must be ",
                      "a positive integer no greater than 65535");
    fi;

    # Create TCP socket
    Info(InfoTCPSockets, 5, "MitM server listening for connections...");
    socket := IO_socket( IO.PF_INET, IO.SOCK_STREAM, "tcp" );
    if socket = fail then
        ErrorNoReturn("ListeningTCPSocket: failed to open socket:\n",
                      LastSystemError().message);
    fi;

    res := IO_bind(socket, IO_make_sockaddr_in(bindaddr, port));
    if res = fail then
        ErrorNoReturn("ListeningTCPSocket: failed to bind:\n",
                      LastSystemError().message);
    fi;

    Info(InfoTCPSockets, 5, "MitM server listening on ", listenname, " ", port);
    # TODO: make the queue length a parameter
    IO_listen(socket, 5);
    return socket;
end);

InstallGlobalFunction( ConnectInputOutputTCPStream,
function( hostname, port )
    local lookup, sock, res, err, fio;

    if not IsString( hostname ) then
        Error("ConnectInputOutputTCPStream: <hostname> must be a string");
    fi;
    if not (IsInt(port) and port >= 0) then
        Error("ConnectInputOutputTCPStream: <port> must be a non-negative integer");
    fi;
    lookup := IO_gethostbyname( hostname );
    if lookup = fail then
        Error("ConnectInputOutputTCPStream: cannot find hostname ", hostname);
    fi;
    sock := IO_socket( IO.PF_INET, IO.SOCK_STREAM, "tcp" );
    res := IO_connect( sock, IO_make_sockaddr_in( lookup.addr[1], port ) );
    if res = fail then
        err := LastSystemError();
        IO_close(sock);
        Error("ConnectInputOutputTCPStream: ", err.message);
    else
        fio := IO_WrapFD( sock, IO.DefaultBufSize, IO.DefaultBufSize );
        return Objectify( InputOutputTCPStreamDefaultType,
                          [ fio, hostname, [ port ], false ] );
    fi;
end);

InstallGlobalFunction( AcceptInputOutputTCPStream,
function(socket_descriptor)
    local fio;
    if not (IsInt(socket_descriptor) and socket_descriptor >= 0) then
        Error("AcceptInputOutputTCPStream: argument must be a non-negative integer");
    fi;
    fio := IO_WrapFD(socket_descriptor, IO.DefaultBufSize, IO.DefaultBufSize);
    return Objectify( InputOutputTCPStreamDefaultType,
                      [ fio, "socket descriptor", [ socket_descriptor ], false ] );
end);

InstallMethod( ViewObj, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
function(stream)
    Print("<");
    if IsClosedStream(stream) then
        Print("closed ");
    fi;
    Print("input/output TCP stream to ",stream![2],":", stream![3][1], ">");
end);

InstallMethod( PrintObj, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
               ViewObj);

InstallMethod( ReadByte, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
function(stream)
    local buf;
    buf := IO_Read( stream![1], 1 );
    if buf = fail or Length(buf) = 0 then
        stream![4] := true;
        return fail;
    else
        stream![4] := true;
        return INT_CHAR(buf[1]);
    fi;
end);

InstallMethod( ReadLine, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
function( stream )
    local sofar, chunk;
    sofar := IO_Read( stream![1], 1 );
    if sofar = fail or Length(sofar) = 0 then
        stream![4] := true;
        return fail;
    fi;
    while sofar[ Length(sofar) ] <> '\n' do
        chunk := IO_Read( stream![1], 1);
        if chunk = fail or Length(chunk) = 0 then
            stream![4] := true;
            return sofar;
        fi;
        Append( sofar, chunk );
    od;
    return sofar;
end);

BindGlobal( "ReadAllIoTCPStream",
function(stream, limit)
    local sofar, chunk, csize;
    if limit = -1 then
        csize := 20000;
    else
        csize := Minimum(20000,limit);
        limit := limit - csize;
    fi;
    sofar := IO_Read(stream![1], csize);
    if sofar = fail or Length(sofar) = 0 then
        stream![4] := true;
        return fail;
    fi;
    while limit <> 0  do
        if limit = -1 then
            csize := 20000;
        else
            csize := Minimum(20000,limit);
            limit := limit - csize;
        fi;
        chunk := IO_Read( stream![1], csize);
        if chunk = fail or Length(chunk) = 0 then
            stream![4] := true;
            return sofar;
        fi;
        Append(sofar,chunk);
    od;
    return sofar;
end);

InstallMethod( ReadAll, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
               stream ->  ReadAllIoTCPStream(stream, -1) );

InstallMethod( ReadAll, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream, IsInt ],
function( stream, limit )
    if limit < 0 then
        Error("ReadAll: negative limit not allowed");
    fi;
    return  ReadAllIoTCPStream(stream, limit);
end);

InstallMethod( WriteByte, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream, IsInt ],
function(stream, byte)
    local ret,s;
    if byte < 0 or 255 < byte  then
        Error( "<byte> must an integer between 0 and 255" );
    fi;
    s := [CHAR_INT(byte)];
    ConvertToStringRep( s );
    ret := IO_Write( stream![1], s );
    if ret <> 1 then
        return fail;
    else
        return true;
    fi;
end);

InstallMethod( WriteLine, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream, IsString ],
function( stream, string )
    return IO_WriteLine( stream![1], string );
end);

InstallMethod( WriteAll, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream, IsString ],
function( stream, string )
    local byte;
    for byte in string  do
        if WriteByte( stream, INT_CHAR(byte) ) <> true  then
            return fail;
        fi;
    od;
    return true;
end);

InstallMethod( IsEndOfStream, "iostream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
stream -> not IO_HasData( stream![1] ) );
# TODO: when does this return true? -MT

InstallMethod( CloseStream, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
function(stream)
    IO_Close( stream![1] );
    SetFilterObj( stream, IsClosedStream );
end);

InstallMethod( FileDescriptorOfStream, "for ioTCPstream",
               [ IsInputOutputTCPStreamRep and IsInputOutputStream ],
               stream -> IO_GetFD( stream![1] ) );


--
-- An LPG Lexer Template Using lpg.jar
--
-- An instance of this template must have a %Export section and the export_terminals option
-- There must be only one non-terminal, the start symbol, for the keywords
-- The action for each keyword should be a call to %setResult(terminal_symbol)
--
-- Macro that may be redefined in an instance of this template
--
--     %eof_char
--
-- B E G I N N I N G   O F   T E M P L A T E   KeywordTemplateF (Similar to KeywordTemplateD)
--
%Options programming_Language=typescript,margin=4
%Options table
%options action-block=("*.ts", "/.", "./")
%options ParseTable=ParseTable
%Options prefix=Char_
%Options single-productions

%Globals
    /.
    import { %prs_type } from ".\/%prs_type";
    import { %sym_type } from ".\/%sym_type";
    import { %exp_type } from ".\/%exp_type";
    ./
%End

--
-- This template requires that the name of the EOF token be set
-- to EOF and that the prefix be "Char_" to be consistent with
-- LexerTemplateD.
--
%Eof
    EOF
%End

%Define
    --
    -- Macro that may be respecified in an instance of this template
    --
    $eof_char /.%sym_type%.%prefix%EOF%suffix%./

    --
    -- Macros useful for specifying actions
    --
    $setResult /.this.keywordKind[%rule_number] = ./

    $Header
    /.
            //
            // Rule %rule_number:  %rule_text
            //
    ./

    $BeginAction /.%Header./

    $EndAction /../

    $BeginJava /.%BeginAction./

    $EndJava /.%EndAction./
%End

%Headers
    /.
    export class %action_type extends %prs_type
    {
        private inputChars : string;
        private   keywordKind  : number[] = new Array(%num_rules + 1);

        public  getKeywordKinds() : number[] { return this.keywordKind; }

        public  lexer(curtok : number, lasttok : number) : number
        {
            let current_kind = %action_type.getKind(this.inputChars.charCodeAt(curtok)),
                act;

            for (act = this.tAction(this.START_STATE, current_kind);
                 act > this.NUM_RULES && act < this.ACCEPT_ACTION;
                 act = this.tAction(act, current_kind))
            {
                curtok++;
                current_kind = (curtok > lasttok
                                       ? %eof_char
                                       : %action_type.getKind(this.inputChars.charCodeAt(curtok)));
            }

            if (act > this.ERROR_ACTION)
            {
                curtok++;
                act -= this.ERROR_ACTION;
            }

            return this.keywordKind[act == this.ERROR_ACTION  || curtok <= lasttok ? 0 : act];
        }

        public setInputChars(inputChars : string ) : void  { this.inputChars = inputChars; }

    ./
%End

%Rules
    /.

        constructor( inputChars : string,  identifierKind : number)
        {
            super();
            this.inputChars = inputChars;
            this.keywordKind[0] = identifierKind;
    ./
%End

%Trailers
    /.
            for (let i : number = 0; i < this.keywordKind.length; i++)
            {
                if (this.keywordKind[i] == 0)
                    this.keywordKind[i] = identifierKind;
            }
        }
    }
    ./
%End

--
-- E N D   O F   T E M P L A T E
--


--/**
-- * <copyright>
-- *
-- * Copyright (c) 2010, 2009 IBM Corporation and others.
-- * All rights reserved.   This program and the accompanying materials
-- * are made available under the terms of the Eclipse Public License v2.0
-- * which accompanies this distribution, and is available at
-- * http://www.eclipse.org/legal/epl-v20.html
-- *
-- * Contributors:
-- *   See (or edit) Notice Declaration below
-- *
-- * </copyright>
-- */
--
-- The Essential OCL KeyWord Lexer
--

%options slr
%options fp=EssentialOCLKWLexer,prefix=Char_
%options noserialize
%options package=org.eclipse.ocl.xtext.essentialocl.parser
%options template=../lpg/KeywordTemplateF.gi
%options export_terminals=("EssentialOCLParsersym.java", "TK_")
%options include_directory="../lpg"

%Import
	KWLexerMapF.gi
%End

%Define

	--
	-- Definition of macros used in the template
	--
	$action_class /.$file_prefix./
	$eof_char /.Char_EOF./
	$copyright_contributions /.*./

%End

%Notice
	/./**
 * Essential OCL Keyword Lexer
 * <copyright>
 *
 * Copyright (c) 2010, 2009 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * Contributors:
 *   IBM - Initial API and implementation
 *   E.D.Willink - Lexer and Parser refactoring to support extensibility and flexible error handling
 *   E.D.Willink - Bug 285633, 292112
 *   Adolfo Sanchez-Barbudo Herrera (Open Canarias) - LPG v 2.0.17 adoption (242153)
 *   Adolfo Sanchez-Barbudo Herrera (Open Canarias) - Introducing new LPG templates (299396)
$copyright_contributions
 * </copyright>
 *
 *
 */
	./
%End

%Globals
	/../
%End

%Export
	self
	if
	then
	else
	endif
	and
	or
	xor
	not
	implies
	let
	in
	true
	false

	null
	invalid

	--
	-- the remainder of the LPG keywords are defined as such for the
	-- purpose of constructing the CST grammar.  They are not OCL
	-- reserved words
	--
	Set
	Bag
	Sequence
	Collection
	OrderedSet

	String
	Integer
	UnlimitedNatural
	Real
	Boolean
	Tuple
	OclAny
	OclVoid
	OclInvalid
%End

%Start
	KeyWord
%End

%Rules

-- The Goal for the parser is a single Keyword

	KeyWord ::=
		s e l f
		/.$BeginAction
			$setResult($_self);
		  $EndAction
		./

		| i f
		/.$BeginAction
			$setResult($_if);
		  $EndAction
		./

		| t h e n
		/.$BeginAction
			$setResult($_then);
		  $EndAction
		./

		| e l s e
		/.$BeginAction
			$setResult($_else);
		  $EndAction
		./

		| e n d i f
		/.$BeginAction
			$setResult($_endif);
		  $EndAction
		./

		| a n d
		/.$BeginAction
			$setResult($_and);
		  $EndAction
		./

		| o r
		/.$BeginAction
			$setResult($_or);
		  $EndAction
		./

		| x o r
		/.$BeginAction
			$setResult($_xor);
		  $EndAction
		./

		| n o t
		/.$BeginAction
			$setResult($_not);
		  $EndAction
		./

		| i m p l i e s
		/.$BeginAction
			$setResult($_implies);
		  $EndAction
		./

		| l e t
		/.$BeginAction
			$setResult($_let);
		  $EndAction
		./

		| i n
		/.$BeginAction
			$setResult($_in);
		  $EndAction
		./

		| t r u e
		/.$BeginAction
			$setResult($_true);
		  $EndAction
		./

		| f a l s e
		/.$BeginAction
			$setResult($_false);
		  $EndAction
		./

		| S e t
		/.$BeginAction
			$setResult($_Set);
		  $EndAction
		./

		| B a g
		/.$BeginAction
			$setResult($_Bag);
		  $EndAction
		./

		| S e q u e n c e
		/.$BeginAction
			$setResult($_Sequence);
		  $EndAction
		./

		| C o l l e c t i o n
		/.$BeginAction
			$setResult($_Collection);
		  $EndAction
		./

		| O r d e r e d S e t
		/.$BeginAction
			$setResult($_OrderedSet);
		  $EndAction
		./

		| S t r i n g
		/.$BeginAction
			$setResult($_String);
		  $EndAction
		./

		| I n t e g e r
		/.$BeginAction
			$setResult($_Integer);
		  $EndAction
		./

		| U n l i m i t e d N a t u r a l
		/.$BeginAction
			$setResult($_UnlimitedNatural);
		  $EndAction
		./

		| R e a l
		/.$BeginAction
			$setResult($_Real);
		  $EndAction
		./

		| B o o l e a n
		/.$BeginAction
			$setResult($_Boolean);
		  $EndAction
		./

		| T u p l e
		/.$BeginAction
			$setResult($_Tuple);
		  $EndAction
		./

		| O c l A n y
		/.$BeginAction
			$setResult($_OclAny);
		  $EndAction
		./

		| O c l V o i d
		/.$BeginAction
			$setResult($_OclVoid);
		  $EndAction
		./

		| O c l I n v a l i d
		/.$BeginAction
			$setResult($_OclInvalid);
		  $EndAction
		./

		| n u l l
		/.$BeginAction
			$setResult($_null);
		  $EndAction
		./

		| i n v a l i d
		/.$BeginAction
			$setResult($_invalid);
		  $EndAction
		./
%End


package main

import (
	"github.com/containous/yaegi/interp"
)

func main() {
	i := interp.New(interp.Opt{})
	i.Use(interp.ExportValue, interp.ExportType)
	i.Eval(`import "github.com/containous/yaegi/interp"`)
	i.Eval(`i := interp.New(interp.Opt{})`)
	i.Eval(`i.Eval("println(42)")`)
}

// Output:
// 42


%Headers
/.
        //
        // These functions are only needed in order to use a lexer as
        // a special purpose "diff" program. To do so, one can invoke
        // the main program below directly from the command line.
        //
        private static int LINES = 0,
                           TOKENS = 1;
        private static int differ_mode = LINES; // default
        private static String extension = "";
        private static int changeCount = 0,
                           insertCount = 0,
                           deleteCount = 0,
                           moveCount = 0;

        private static void compareFiles(String old_file, String new_file)
        {
            try
            {
                $action_type old_lexer, new_lexer;
                if (old_file.equals(""))
                {
                    char [] input_chars = new char[0];
                    old_lexer = new $action_type(input_chars, "null_file");
                }
                else old_lexer = new $action_type(old_file);

                if (new_file.equals(""))
                {
                    char [] input_chars = new char[0];
                    new_lexer = new $action_type(input_chars, "null_file");
                }
                else new_lexer = new $action_type(new_file);

                PrsStream old_stream = new PrsStream(old_lexer.getILexStream());
                old_lexer.lexer(old_stream);

                PrsStream new_stream = new PrsStream(new_lexer.getILexStream());
                new_lexer.lexer(new_stream);

                Differ diff = (differ_mode == TOKENS ? (Differ) new DifferTokens(old_stream, new_stream)
                                                     : (Differ) new DifferLines(old_stream, new_stream));
                diff.compare();

                if (diff.getChangeCount() > 0)
                {
                    diff.outputChanges();

                    changeCount += diff.getChangeCount();
                    insertCount += (diff.getInsertCount() + diff.getReplaceInsertCount());
                    deleteCount += (diff.getDeleteCount() + diff.getReplaceDeleteCount());
                    moveCount += diff.getMoveCount();
                }
            }
            catch (Exception e)
            {
                System.err.println(e.getMessage());
                e.printStackTrace();
            }
        }

        private static void compareDirectories(java.io.File old_dir, java.io.File new_dir)
        {
            try
            {
                java.io.File old_file[] = old_dir.listFiles(),
                             new_file[] = new_dir.listFiles();
                java.util.HashMap old_map = new java.util.HashMap();
                for (int i = 0; i < old_file.length; i++)
                {
                    String name = old_file[i].getName();
                    if (old_file[i].isDirectory() || name.endsWith(extension))
                        old_map.put(name, old_file[i]);
                }

                for (int i = 0; i < new_file.length; i++)
                {
                    java.io.File file = (java.io.File) old_map.get(new_file[i].getName());
                    if (file != null)
                    {
                        old_map.remove(new_file[i].getName());

                        if (file.isDirectory() && new_file[i].isDirectory())
                             compareDirectories(file, new_file[i]);
                        else compareFiles(file.getPath(), new_file[i].getPath());
                    }
                    else if (new_file[i].isDirectory() ||
                             new_file[i].getName().endsWith(extension))
                    {
                        String s = new_file[i].getName() +
                                   " found in directory " +
                                   new_dir.getPath() +
                                   " does not exist in directory " +
                                   old_dir.getPath();
                        System.err.println("*Warning: " + s);

                        if (! new_file[i].isDirectory())
                            compareFiles("", new_file[i].getPath());
                    }
                }

                for (java.util.Iterator i = old_map.entrySet().iterator(); i.hasNext(); )
                {
                    java.util.Map.Entry e = (java.util.Map.Entry) i.next();
                    java.io.File file = (java.io.File) e.getValue();

                    String s = file.getName() +
                               " not found in directory " +
                               new_dir.getPath();
                    System.err.println("*Warning: " + s);

                    if (! file.isDirectory())
                        compareFiles(file.getPath(), "");
                }
            }
            catch (Exception e)
            {
                System.err.println(e.getMessage());
                e.printStackTrace();
            }
        }

        public static void main(String[] args)
        {
            String new_file = null,
                   old_file = null;
            boolean help = false;

            int i;
            for (i = 0; i < args.length; i++)
            {
                if (args[i].charAt(0) == '-')
                {
                    if (args[i].equals("-ext"))
                         extension = (i + 1 < args.length ? args[++i] : "");
                    else if (args[i].equals("-h"))
                         help = true;
                    else if (args[i].equals("-l"))
                         differ_mode = LINES;
                    else if (args[i].equals("-t"))
                         differ_mode = TOKENS;
                }
                else break;
            }
            if (i < args.length)
            {
                new_file = args[i++];
                old_file = new_file; // assume only one file is specified
            }
            if (i < args.length)
                old_file = args[i++];
            for (; i < args.length; i++)
                System.err.println("Invalid argument: " + args[i]);

            if (help || (new_file == null &&  old_file == null))
            {
                System.out.println();
                System.out.println("Usage: diff [OPTION]... file1 [file2]");
                System.out.println("Compute stats for file1 or compare file1 to file2 statement by statement.");
                System.out.println();
                System.out.println("  -ext s -- if file1 and file2 are directories, compare only files that end\n" +
                                   "            with the extension (suffix) s.");
                System.out.println("  -h     -- print this help message");
                System.out.println("  -l     -- compare line by line instead of statement by statement");
                System.out.println("  -t     -- compare token by token instead of statement by statement");
            }
            else if (old_file.equals(new_file))
            {
                java.io.File old_dir = new java.io.File(old_file);
                // if (old_dir.isDirectory())
                //     computeStats(old_dir);
                // else computeStats(old_file);

                System.out.println("*** No difference ***");
                // System.out.println("    Number of files: " + fileCount);
                // System.out.println("    Number of lines: " + lineCount);
                // System.out.println("    Number of types (classes/interfaces): " + (classCount + interfaceCount) + " (" + classCount + "/" + interfaceCount + ")");
                // System.out.println("    Number of statements: " + statementCount);
                // System.out.println("    Number of braces (left/right): (" + leftBraceCount + "/" + rightBraceCount + ")");
            }
            else
            {
                java.io.File old_dir = new java.io.File(old_file),
                     new_dir = new java.io.File(new_file);
                if (old_dir.isDirectory() && new_dir.isDirectory())
                     compareDirectories(old_dir, new_dir);
                else compareFiles(old_file, new_file);

                if (changeCount == 0)
                    System.out.println("***** No difference *****");
                else
                {
                    System.out.println("***** " +
                                       changeCount +
                                       " different " +
                                       (changeCount == 1 ? "section" : "sections") + " *****");
                    System.out.println("    " + moveCount    + " statements moved");
                    System.out.println("    " + insertCount  + " statements inserted");
                    System.out.println("    " + deleteCount  + " statements deleted");
                }
            }

            return;
        }
./
%End


# Several ways to do it
"Goodbye, World!";

Print("Goodbye, World!\n"); # No EOL appended

Display("Goodbye, World!");

f := OutputTextUser();
WriteLine(f, "Goodbye, World!\n");
CloseStream(f);


# Copyright (c) 2018-2021, Carnegie Mellon University
# See LICENSE for details

Import(platforms, profiler, code);
Import(platforms.sse, platforms.avx, platforms.intel);

Class(OSInfo, rec(osname := "",
    is8Bit := False,
    is16bit := False,
    is32bit := False,
    is64bit := False,
    isWindows := False,
    isLinux := False,
    isCygwin := False,
    isDarwin := False,
    info := self >> Cond(IsBound(self.vendor), Print(self.vendor, " ", self.osname), Print(self.osname)),
    useColor := True
));

Class(OSWindows, OSInfo, rec(
    setTitle := meth(self, title)
        Exec(Concat("title ", title));
    end,
    vendor := "Microsoft",
    isWindows := True,
    useColor := False
));

Class(OSWindows32, OSWindows, rec(is16bit := True, is32bit := True));
Class(OSWindows64, OSWindows, rec(is16bit := True, is32bit := True, is64bit :=True));
Class(OSLinux32, OSInfo, rec(osname := "Linux32", is32bit := True, isLinux := True));
Class(OSLinux64, OSInfo, rec(osname := "Linux64", is32bit := True, is64bit := True, isLinux := True));
Class(OSArmLinux, OSInfo, rec(osname := "GNU/Linux", is16bit := True, is32bit := True, isLinux := True));
Class(OSCygwin32, OSInfo, rec(osname := "Cygwin32", is32bit := True, isCygwin := True));
Class(OSDarwin, OSInfo, rec(osname := "OSX/Darwin", is32bit := True, is64bit := True, isDarwin := True));

SupportedOSs := rec(
    WindowsNT4 := CopyFields(OSWindows32, rec(osname := "Windows NT 4.0")),
    Windows2000 := CopyFields(OSWindows32, rec(osname := "Windows 2000")),
    WindowsXP32 := CopyFields(OSWindows32, rec(osname := "WindowsXP 32-bit")),
    WindowsXP64 := CopyFields(OSWindows64, rec(osname := "WindowsXP 64-bit")),
    WindowsVista := CopyFields(OSWindows64, rec(osname := "Windows Vista")),
    Windows7 := CopyFields(OSWindows64, rec(osname := "Windows 7")),
    Windows8 := CopyFields(OSWindows64, rec(osname := "Windows 8")),
    Linux32 := OSLinux32,
    Linux64 := OSLinux64,
    Cygwin32 := OSCygwin32,
    Darwin := OSDarwin,
    ArmLinux := OSArmLinux,
);

Class(CPUInfo, rec(
    hasDouble := True,
    hasFloat := True,
    intSize := 32,
    cores := 1,
    cpuname := "",
    vendor := "",
    info := self >> Chain(
        Print(Cond(IsBound(self.vendor), Print(self.vendor, " ", self.cpuname), Print(self.osname))),
        When(IsBound(self.freq) and self.freq > 0, Print(" at ", self.freq, " MHz")),
        When(self.cores > 1, Print(", ", self.cores, " cores")),
        When(IsBound(self.SIMDname), Print(", ", self.SIMDname))
    )
));

Class(IntelCPU, CPUInfo, rec(
    vendor := "Intel",
    getSimdIsa := (self, dt) >> When(self.SIMD().hasAVX(),
        Cond(
            dt = T_Real(32), AVX_8x32f,
            dt = T_Real(64), AVX_4x64f,
            dt),
        Cond(
            dt = T_Real(32), SSE_4x32f,
            dt = T_Real(64), SSE_2x64f,
            dt)
    ),
    getOpts := arg >> IAGlobals.getOpts(Drop(arg, 1))
));

Class(AMDCPU, CPUInfo, rec(
    vendor := "AMD"
));

Class(STICPU, CPUInfo, rec(
    vendor := "STI"
));

Class(PowerPC, CPUInfo, rec(
    vendor := "IBM"
));

Class(DPA, CPUInfo, rec(
       vendor := "CMU"
));

Class(ARM, CPUInfo, rec(
       vendor := "Raspberry Pi Foundation"
));

SupportedCPUs := rec(
    Pentium := rec(),
    PentiumPro := rec(),
    PentiumII := rec(),
    PentiumIII := rec(),
    Pentium4 := Class(Pentium4, IntelCPU, rec(
        cpuname := "Pentium4",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=False)),
        SIMDname := "SSE2",
        cores := 1,
        default_lang := "c.icl.opt.pentium4"
    )),
    Pentium4Extreme := Class(Pentium4Extreme, IntelCPU, rec(
        cpuname := "Pentium4Extreme",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True)),
        SIMDname := "SSE3",
        cores := 1,
        default_lang := "c.icl.opt.pentium4extreme"
    )),
    PentiumD := rec(),
    Xeon := Class(Xeon, IntelCPU, rec(
        cpuname := "Xeon",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True, hasSSE4_1 := True, hasSSE4_2 := True)),
        SIMDname := "SSE4.2",
        cores := 4,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxS"))
        ),
        default_lang := "c.icl.opt.corei7",
        smp_lang := "c.icl.smp_corei7",
        OpenMP_lang := "c.icl.openmp_corei7"
    )),
    XeonMP := rec(),
    PentiumM := Class(PentiumM, IntelCPU, rec(
        cpuname := "PentiumM",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=False)),
        SIMDname := "SSE2",
        cores := 1,
        default_lang := "c.icl.opt.pentiumM"
    )),
    CoreDuo := Class(CoreDuo, IntelCPU, rec(
        cpuname := "CoreDuo",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True)),
        SIMDname := "SSE3",
        cores := 2,
        profile := rec(
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxKWP")),
            threads := (arg) -> When(LocalConfig.osinfo.isWindows(),
        CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxKWP")),
        CopyFields(default_profiles.linux_x86_threads, rec(CFLAGS := arg -> "-msse3"))
)
        ),
        default_lang := "c.icl.opt.core",
        smp_lang := "c.icl.smp_core",
        OpenMP_lang := "c.icl.openmp_core"
    )),
    Core2Duo := Class(Core2Duo, IntelCPU, rec(
        cpuname := "Core2Duo",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True)),
        SIMDname := "SSSE3",
        cores := 2,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxSSSE3")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxKWP")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxKWP"))
        ),
        default_lang := "c.icl.opt.core2",
        smp_lang := "c.icl.smp_core2",
        OpenMP_lang := "c.icl.openmp_core2"
    )),
    Core2Quad := Class(Core2Quad, IntelCPU, rec(
        cpuname := "Core2 Extreme",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True)),
        SIMDname := "SSSE3",
        cores := 4,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxSSSE3")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxKWP")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxKWP"))
        ),
        default_lang := "c.icl.opt.core2",
        smp_lang := "c.icl.smp_core2",
        OpenMP_lang := "c.icl.openmp_core2"
    )),

    DPABasic := Class(DPABasic, DPA, rec(
	    cpuname := "DPABasic",
	    cores := 1,
	    SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec( numregs := 32)),
	    )),

    Core2Penryn := Class(Core2Penryn, IntelCPU, rec(
        cpuname := "Core2 Penryn",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True, hasSSE4_1 := True)),
        SIMDname := "SSE4.1",
        cores := 4,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxS"))
        ),
        default_lang := "c.icl.opt.core2p",
        smp_lang := "c.icl.smp_core2p",
        OpenMP_lang := "c.icl.openmp_core2p"
    )),
    Core_i7 := Class(Core_i7, IntelCPU, rec(
        cpuname := "Core i7",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True, hasSSE4_1 := True, hasSSE4_2 := True)),
        SIMDname := "SSE4.2",
        cores := 4,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxS"))
        ),
        default_lang := "c.icl.opt.corei7",
        smp_lang := "c.icl.smp_corei7",
        OpenMP_lang := "c.icl.openmp_corei7"
    )),
	# specifically for Spiral FFT GPL 1.0
    Core_AVX := Class(Core_AVX, IntelCPU, rec(
        cpuname := "Intel Core with AVX",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True, hasSSE4_1 := True, hasSSE4_2 := True, hasAVX := True)),
        SIMDname := "AVX",
        cores := 4,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxS"))
        ),
        default_lang := "c.icl.opt.corei7",
        smp_lang := "c.icl.smp_corei7",
        OpenMP_lang := "c.icl.openmp_corei7"
    )),
	# specifically for Spiral FFT GPL 1.0
    Core_no_AVX := Class(Core_no_AVX, IntelCPU, rec(
        cpuname := "Intel Core without AVX",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True, hasSSE4_1 := True, hasSSE4_2 := True, hasAVX := False)),
        SIMDname := "SSE4.2",
        cores := 4,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxS"))
        ),
        default_lang := "c.icl.opt.corei7",
        smp_lang := "c.icl.smp_corei7",
        OpenMP_lang := "c.icl.openmp_corei7"
    )),
    Core_i7U := Class(Core_i7U, IntelCPU, rec(
        cpuname := "Core i7 Ultrabook",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True, hasSSE4_1 := True, hasSSE4_2 := True, hasAVX := True)),
        SIMDname := "AVX",
        cores := 2,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxS"))
        ),
        default_lang := "c.icl.opt.corei7",
        smp_lang := "c.icl.smp_corei7",
        OpenMP_lang := "c.icl.openmp_corei7"
    )),
    Core_i5U := Class(Core_i5U, IntelCPU, rec(
        cpuname := "Core i5U",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True, hasSSE4_1 := True, hasSSE4_2 := True)),
        SIMDname := "SSE4.2",
        cores := 2,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxS"))
        ),
        default_lang := "c.icl.opt.corei7",
        smp_lang := "c.icl.smp_corei7",
        OpenMP_lang := "c.icl.openmp_corei7"
    )),
    Core_i5_SandyBridge := Class(Core_i5_SandyBridge, IntelCPU, rec(
        cpuname := "Core i5 SandyBridge",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True, hasSSSE3:=True, hasSSE4_1 := True, hasSSE4_2 := True, hasAVX := True)),
        SIMDname := "AVX",
        cores := 4,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxS")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxS"))
        ),
    )),

    Itanium := rec(),
    Itanium2 := rec(),
    Itanium3 := rec(),
    MPC_G4 := rec(),
    PPC905_G5 := rec(),
    XScale := rec(),
    PowerPC405 := rec(),
    Athlon := rec(),
    AthlonXP := rec(),
    Opteron := rec(),
    OpteronDual := rec(),
    OpteronQuad := Class(OpteronQuad, AMDCPU, rec(
        cpuname := "Dual Opteron 2220",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasMMX := True, hasSSE:=True, hasSSE2:=True, hasSSE3:=True)),
        SIMDname := "SSE3",
        cores := 4,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxSSSE3")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "/O3 /G7 /QxKWP")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> "/O3 /G7 /QxKWP"))
        ),
        default_lang := "c.icl.opt.opteron2220"
    )),
    CellBE := Class(CellBE, STICPU, rec(
        cpuname := "Cell BE",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasSPU := True)),
        SIMDname := "SIMD-SPU",
        cores := 9,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> ""))
        ),
        default_lang := ""
   )),
   CellBEPS3 := Class(CellBEPS3, STICPU, rec(
        cpuname := "Cell BE (PS3)",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasSPU := True)),
        SIMDname := "SIMD-SPU",
        cores := 6,
        profile := rec(
            EM64T := (arg) -> CopyFields(default_profiles.win_x64_icc, rec(CFLAGS := arg -> "")),
            IA32 := (arg) -> CopyFields(default_profiles.win_x86_icc, rec(CFLAGS := arg -> "")),
            threads := (arg) -> CopyFields(default_profiles.win_x86_icc_threads, rec(CFLAGS := arg -> ""))
        ),
        default_lang := "",
        getSimdIsa := dt -> Cond(
            dt = T_Real(32), platforms.cellSPU.spu_4x32f,
            dt = T_Real(64), platforms.cellSPU.spu_2x64f,
            dt),
   )),
   PowerPC970 := Class(PowerPC970, PowerPC, rec(
        cpuname := "PowerPC 970 (G5)",
        SIMD := () -> CopyFields(platforms.SIMDArchitectures, rec(hasAltiVec := true)),
        SIMDname := "AltiVec",
        cores := 1,
        profile := rec(
        ),
        default_lang := ""
   )),

   ARMV7L := Class(ARMV7L, ARM, rec(
       cpuname := "ARMV7L",
       cores := 4,
   )),

   BlueGeneL := rec()
);


# Copyright (c) 2018-2021, Carnegie Mellon University
# See LICENSE for details

TestFailExit := function()
    Print("TEST FAILED\n");
    Exit(-1);
end;

TestSkipExit := function()
    Print("Skipping test\n");
    Exit(86);
end;

#F  GetBasicProfilerTestFName (<pf>)
#F      Get the filename associated with Basic Profiler Test
#F      if <pf> return Success file name
#F      otherwise return Failure file name

GetBasicProfilerTestFName := function(pf)
    local path, sep;
    sep  := Conf("path_sep");
    path := Conf("spiral_dir");
    path := Concat(path, sep, "build");
    if pf then
        path := Concat(path, sep, "PROFILER_RUN_SUCCESS");
    else
        path := Concat(path, sep, "PROFILER_RUN_FAILED");
    fi;
    return path;
end;

#F  ClearBasicProfilerTestResults () -- remove results of prior run(s) of basic profiler test

ClearBasicProfilerTestResults := function()
    SysRemove(GetBasicProfilerTestFName(true));
    SysRemove(GetBasicProfilerTestFName(false));
    return;
end;

#F  MarkBasicProfilerTest (<pf>)
#F      Mark (create the filename) associated with Basic Profiler Test
#F      if <pf> ==> test successed, use success file name
#F      otherwise ==> test failed, use Failure file name

MarkBasicProfilerTest := function(pf)
    local path;
    path := GetBasicProfilerTestFName(pf);
    PrintTo(path, "");
    return;
end;

#F  CheckBasicProfilerTest () -- Return True if basic profiler test passed, otherwise, False

CheckBasicProfilerTest := function()
    local res, file;
    file := GetBasicProfilerTestFName(true);
    res  := CheckFileExists(file, "");

    if SysVerbose() > 0 then
        Print("Marker file: ", file);
        if res then PrintLine(" Found, return true"); else PrintLine(" NOT Found"); fi;
    fi;
    if res then return res; fi;

    file := GetBasicProfilerTestFName(false);
    res := CheckFileExists(file, "");
    if SysVerbose() > 0 then
        Print("Marker file: ", file);
        if res then PrintLine(" Found, return false"); else PrintLine(" NOT Found"); fi;
    fi;
    return false;
end;


# This is another ugly hack to make the GAP Help System
# play ball. Let us please fix this soon.
# TODO: This is now broken because we got rid of parsing
#       on the python side. HELP now should result
#       in a record that can be sent back to jupyter
#       as a JSON string
HELP_VIEWER_INFO.jupyter_online :=
    rec(
         type := "url",
         show := function( data )
             # data[1] is the text preceding the hyperlink (name of the help book),
             # data[2] is the text to be linked, and data[3] is the URL
             local p,r;

             p := data[3];

             for r in GAPInfo.RootPaths do
                 p := ReplacedString(data[3], r, "https://www.gap-system.org/Manuals/");
             od;
             return JupyterRenderable( rec( ("text/html") := Concatenation( data[1], ": ", data[2], "") )
                                     , rec( ) );
         end
        );

HELP_VIEWER_INFO.jupyter_local :=
    rec( type := "url",
         show := function( data )
             # data[1] is the text preceding the hyperlink (name of the help book),
             # data[2] is the text to be linked, and data[3] is the URL
             local p,r;

             p := data[3];

             for r in GAPInfo.RootPaths do
                 p := ReplacedString(data[3], r, "/");
             od;
             return JupyterRenderable( rec( ("text/html") := Concatenation( data[1], ": ", data[2], "") )
                                     , rec( ) );
         end);

#############################################################################
##
#F  GET_HELP_URL( <match> ) . . . . . .  print the url for the help section
##
##  Based on HELP_PRINT_MATCH
##
##  <match> is [book, entrynr]
##
InstallGlobalFunction(GET_HELP_URL, function(match)
local book, entrynr, viewer, hv, pos, type, data;
  book := HELP_BOOK_INFO(match[1]);
  entrynr := match[2];
  viewer:= UserPreference("HelpViewers");
  if HELP_LAST.NEXT_VIEWER = false then
    hv := viewer;
  else
    pos := Position( viewer, HELP_LAST.VIEWER );
    if pos = fail then
      hv := viewer;
    else
      hv := viewer{Concatenation([pos+1..Length(viewer)],[1..pos])};
    fi;
    HELP_LAST.NEXT_VIEWER := false;
  fi;
  for viewer in hv do
    # type of data we need now depends on help viewer
    type := HELP_VIEWER_INFO.(viewer).type;
    # get the data via appropriate handler
    data := HELP_BOOK_HANDLER.(book.handler).HelpData(book, entrynr, type);
    if data <> fail then
      # show the data
      return HELP_VIEWER_INFO.(viewer).show(
        [ book.bookname, StripEscapeSequences(book.entries[entrynr][1]), data]);
          # name of the help book, the text to be linked, and the URL
    else
        return JupyterRenderable( rec( ("text/html") := Concatenation( book.bookname, ": "
                                                                       , StripEscapeSequences(book.entries[entrynr][1])
                                                                       , " - no html help available. Please check other formats!" ) )
                                , rec( ) );
    fi;
    HELP_LAST.VIEWER := viewer;
  od;
  HELP_LAST.BOOK := book;
  HELP_LAST.MATCH := entrynr;
  HELP_LAST.VIEWER := viewer;
  return true;
end);

InstallGlobalFunction(JUPYTER_HELP_SHOW_MATCHES, function( books, topic, frombegin )
local   exact,  match,  x,  lines,  cnt,  i,  str,  n, res;

  # first get lists of exact and other matches
  x := HELP_GET_MATCHES( books, topic, frombegin );
  exact := x[1];
  match := x[2];

  # no topic found
  if 0 = Length(match) and 0 = Length(exact)  then
    Print( "Help: no matching entry found\n" );
    return false;

  # one exact or together one topic found
  elif 1 = Length(exact) or (0 = Length(exact) and 1 = Length(match)) then
    if Length(exact) = 0 then exact := match; fi;
    i := exact[1];
    return GET_HELP_URL(i);

  # more than one topic found, show overview in pager
  else
    lines :=
        ["","Help: several entries match this topic - type ?2 to get match [2]\n"];
        # there is an empty line in the beginning since `tail' will start from line 2
    HELP_LAST.TOPICS:=[];
    cnt := 0;
    # show exact matches first
    match := Concatenation(exact, match);
    res:="";
    for i  in match  do
      cnt := cnt+1;
      topic := Concatenation(i[1].bookname,": ",i[1].entries[i[2]][1]);
      Add(HELP_LAST.TOPICS, i);
      Append(res, GET_HELP_URL(i)!.data.("text/html"));
      Append(res, "
");
    od;
    return JupyterRenderable( rec( ("text/html") := res )
                            , rec( ) );
  fi;
end);

InstallGlobalFunction(JUPYTER_HELP, function( str )
  local origstr, nwostr, p, book, books, move, add;

  origstr := ShallowCopy(str);
  nwostr := NormalizedWhitespace(origstr);

  # extract the book
  p := Position( str, ':' );
  if p <> fail  then
      book := str{[1..p-1]};
      str  := str{[p+1..Length(str)]};
  else
      book := "";
  fi;

  # normalizing for search
  book := SIMPLE_STRING(book);
  str := SIMPLE_STRING(str);

  # we check if `book' MATCH_BEGINs some of the available books
  books := Filtered(HELP_KNOWN_BOOKS[1], bn-> MATCH_BEGIN(bn, book));
  if Length(book) > 0 and Length(books) = 0 then
    Print("Help: None of the available books matches (try: '?books').\n");
    return;
  fi;

  # function to add a topic to the ring
  move := false;
  add  := function( books, topic )
      if not move  then
          HELP_RING_IDX := (HELP_RING_IDX+1) mod HELP_RING_SIZE;
          HELP_BOOK_RING[HELP_RING_IDX+1]  := books;
          HELP_TOPIC_RING[HELP_RING_IDX+1] := topic;
      fi;
  end;

  # if the topic is empty show the last shown one again
  if  book = "" and str = ""  then
       if HELP_LAST.BOOK = 0 then
         HELP("Tutorial: Help");
       else
         return GET_HELP_URL( [HELP_LAST.BOOK, HELP_LAST.MATCH] );
       fi;
       return;

  # if topic is "&" shobn;w last topic again, but with next viewer in viewer
  # list, or with last viewer again if there is no next one
  elif book = "" and str = "&" and Length(nwostr) = 1 then
       if HELP_LAST.BOOK = 0 then
         HELP("Tutorial: Help");
       else
         HELP_LAST.NEXT_VIEWER := true;
         return GET_HELP_URL( [HELP_LAST.BOOK, HELP_LAST.MATCH] );
       fi;
       return;

  # if the topic is '-' we are interested in the previous search again
  elif book = "" and str = "-" and Length(nwostr) = 1  then
      HELP_RING_IDX := (HELP_RING_IDX-1) mod HELP_RING_SIZE;
      books := HELP_BOOK_RING[HELP_RING_IDX+1];
      str  := HELP_TOPIC_RING[HELP_RING_IDX+1];
      move := true;

  # if the topic is '+' we are interested in the last section again
  elif book = "" and str = "+" and Length(nwostr) = 1  then
      HELP_RING_IDX := (HELP_RING_IDX+1) mod HELP_RING_SIZE;
      books := HELP_BOOK_RING[HELP_RING_IDX+1];
      str  := HELP_TOPIC_RING[HELP_RING_IDX+1];
      move := true;
  fi;

  # number means topic from HELP_LAST.TOPICS list
  if book = "" and ForAll(str, a-> a in "0123456789") then
      HELP_SHOW_FROM_LAST_TOPICS(Int(str));

  # if the topic is '<' we are interested in the one before 'LastTopic'
  elif book = "" and str = "<" and Length(nwostr) = 1  then
      HELP_SHOW_PREV();

  # if the topic is '>' we are interested in the one after 'LastTopic'
  elif book = "" and str = ">" and Length(nwostr) = 1  then
      HELP_SHOW_NEXT();

  # if the topic is '<<' we are interested in the previous chapter intro
  elif book = "" and str = "<<"  then
      HELP_SHOW_PREV_CHAPTER();

  # if the topic is '>>' we are interested in the next chapter intro
  elif book = "" and str = ">>"  then
      HELP_SHOW_NEXT_CHAPTER();

  # if the subject is 'Welcome to GAP' display a welcome message
  elif book = "" and str = "welcome to gap"  then
      if HELP_SHOW_WELCOME(book)  then
          add( books, "Welcome to GAP" );
      fi;

  # if the topic is 'books' display the table of books
  elif book = "" and str = "books"  then
      if HELP_SHOW_BOOKS()  then
          add( books, "books" );
      fi;

  # if the topic is 'chapters' display the table of chapters
  elif str = "chapters"  or str = "contents" or book <> "" and str = "" then
      if ForAll(books, b->  HELP_SHOW_CHAPTERS(b)) then
        add( books, "chapters" );
      fi;

  # if the topic is 'sections' display the table of sections
  elif str = "sections"  then
      if ForAll(books, b-> HELP_SHOW_SECTIONS(b)) then
        add(books, "sections");
      fi;

  # if the topic is '?<string>' search the index for any entries for
  # which <string> is a substring (as opposed to an abbreviation)
  elif Length(str) > 0 and str[1] = '?'  then
      str := str{[2..Length(str)]};
      NormalizeWhitespace(str);
      return HELP_SHOW_MATCHES( books, str, false);

  # search for this topic
  elif IsJupyterRenderable( HELP_SHOW_MATCHES( books, str, true ) ) then
      return HELP_SHOW_MATCHES( books, str, true );
  elif origstr in NAMES_SYSTEM_GVARS then
      Print( "Help: '", origstr, "' is currently undocumented.\n",
             "      For details, try ?Undocumented Variables\n" );
  elif book = "" and
                 ForAny(HELP_KNOWN_BOOKS[1], bk -> MATCH_BEGIN(bk, str)) then
      Print( "Help: Are you looking for a certain book? (Trying '?", origstr,
             ":' ...\n");
      return HELP( Concatenation(origstr, ":") );
  else
     # seems unnecessary, since some message is already printed in all
     # cases above (?):
     # Print( "Help: Sorry, could not find a match for '", origstr, "'.\n");
  fi;
end);

# Load some help stuff (Experimental)
InstallGlobalFunction(JUPYTER_FindManSection,
function(file, name)
    local xml, sections, p, s, res;
    xml := ParseTreeXMLFile(file);
    CheckAndCleanGapDocTree(xml);
    sections := XMLElements(xml, "ManSection");;
    res := [];
    for s in sections do
        if IsBound(s.content) then
            p := PositionProperty(s.content, x ->
                                               IsBound(x.attributes) and
                                             IsBound(x.attributes.Name) and
                                             x.attributes.Name = name);
            if p <> fail then
                Add(res, s);
            fi;
        fi;
    od;
    return res;
end);


#
# francy: Interactive Discrete Mathematics in GAP
#

#############################################################################
##
#M  FrancyMessage( <messageType>, <string>, <string> )  . .  create a Label
##
InstallMethod(FrancyMessage,
  "message type, a title, a value",
  true,
  [IsFrancyMessageType,
   IsString,
   IsString],
  0,
function(messageType, title, value)
local id;
id := GenerateID();
  return Objectify(FrancyMessageObjectType, rec(
    id    := id,
    type  := messageType!.value,
    title := title,
    text  := value
  ));
end);

InstallOtherMethod(FrancyMessage,
  "a title, a value",
  true,
  [IsString,
   IsString],
  0,
function(title, value)
  return FrancyMessage(FrancyMessageType.DEFAULT, title, value);
end);

InstallOtherMethod(FrancyMessage,
  "message type, a value",
  true,
  [IsFrancyMessageType,
   IsString],
  0,
function(messageType, value)
  return FrancyMessage(messageType, "", value);
end);

InstallOtherMethod(FrancyMessage,
  "a value",
  true,
  [IsString],
  0,
function(value)
  return FrancyMessage(FrancyMessageType.DEFAULT, "", value);
end);


--
-- The Java KeyWord Lexer
--
%Options fp=CncKWLexer
%options package=CnCParser
%options template=KeywordTemplateF.gi

%Notice
/.
//
// This file is part of the CNC-C implementation and
// distributed under the Modified BSD License.
// See LICENSE for details.
//
// I AM A GENERATED FILE. PLEASE DO NOT CHANGE ME!!!
//
./
%End

%Include
    KWLexerFoldedCaseMapF.gi
%End

%Export

    T_ENV
    T_UNSIGNED
    T_STRUCT

%End

%Terminals
    a    b    c    d    e    f    g    h    i    j    k    l    m
    n    o    p    q    r    s    t    u    v    w    x    y    z
%End

%Start
    KeyWord
%End

%Rules

    KeyWord ::= e n v /.$setResult($_T_ENV);./
    KeyWord ::= s t r u c t /.$setResult($_T_STRUCT);./
    KeyWord ::= u n s i g n e d /.$setResult($_T_UNSIGNED);./

%End


%Define
    $kw_lexer_class /.NoKWLexer./
    $_IDENTIFIER /.0./
%End
%Headers
    --
    -- Additional methods for the action class not provided in the template
    --
    /.
          export   class NoKWLexer
        {
            public  getKeywordKinds() :number[]{ return null; }

            public  lexer(curtok : number, lasttok : number): number { return 0; }

            public  setInputChars(inputChars : string) : void{ }

            public  getKind(c : number) : number{ return 0; }

            public NoKWLexer(inputChars : string,  identifierKind : number) { }
        }
    ./
%End

%Import
    LexerBasicMapF.gi
%End


#
# francy: Interactive Discrete Mathematics in GAP
#

#############################################################################
##
#M  PrintObj( <obj> ) . . . . . . . . . . . . .  override for IsFrancyObjects
##
InstallOtherMethod(PrintObj,
  "a francy object",
  true,
  [IsFrancyObject],
  0,
function(object)
  Print(Sanitize(object));
end);

#############################################################################
##
#M  ViewString( <obj> )  . . . . . . . . . . . . override for IsFrancyObjects
##
InstallOtherMethod(ViewString,
  "a francy object",
  true,
  [IsFrancyObject],
  0,
function(object)
  return (Concatenation( "<",
      CategoriesOfObject( object )[1],
      "/", CategoriesOfObject( object )[2], ">"));
end);

#############################################################################
##
#M  JUPYTER_ViewString( <obj> )  . . . . . . . . override for IsFrancyObjects
##
InstallOtherMethod(JUPYTER_ViewString,
  "a francy object",
  true,
  [IsFrancyObject],
  0,
  ViewString
);

#############################################################################
##
#M  Sanitize( <obj> )  . . . . . . . . simple properties clone for FrancyObjects
##
## This method will clone a FrancyObject and return a record, traversing all the
## components and converting when appropriate.
##
## This method removes components of type IsFunction, as they can't be
## converted to JSON string, converts lists of objects into lists of strings
## and everything else that is not a FrancyObject and therefore unknown!
##
InstallMethod(Sanitize,
  "an object",
  true,
  [IsObject],
  0,
function(object)
  return Sanitize(object, rec());
end);

#############################################################################
##
#M  Sanitize( <obj> )  . . . . . . . . simple properties clone for Records
##
## This method will clone a FrancyObject into the given record
##
InstallOtherMethod(Sanitize,
  "an object, a record",
  true,
  [IsObject,
   IsRecord],
  0,
function(object, record)
  local component, copy, tmp;
  copy := StructuralCopy(object);
  for component in NamesOfComponents(copy) do
    if IsRecord(copy!.(component)) or IsFrancyObject(copy!.(component)) then
      record!.(component) := rec();
      Sanitize(copy!.(component), record!.(component));
    elif IsList(copy!.(component)) and not IsString(copy!.(component)) then
      record!.(component) := [];
      Sanitize(copy!.(component), record!.(component));
    elif IsFunction(copy!.(component)) then
      record!.(component) := NameFunction(copy!.(component));
    else
      record!.(component) := copy!.(component);
    fi;
  od;
  return record;
end);

#############################################################################
##
#M  Sanitize( <obj> )  . . . . . . . . simple properties clone for Records
##
## This method will return a sanitized list from another list
##
InstallOtherMethod(Sanitize,
  "a list, another list",
  true,
  [IsList,
   IsList],
  0,
function(list, result)
  local item;
  for item in list do
    # well, everything that is important for the client is in records
    # everything inside arrays we just convert to string...
    # ...if you wonder, these are most likely known arguments that are stored
    # in order to execute callbacks, so the client does nothing with them
    Add(result, String(item));
  od;
  return result;
end);

#############################################################################
##
#O  MergeRecord( <obj>, <obj> )  . . . . . . . . simple properties merge
##
InstallMethod(MergeObjects,
  "an object, another object",
  true,
  [IsFrancyObject, IsFrancyObject],
  0,
function(dst, src)
  local name;
  for name in NamesOfComponents(src) do
    dst!.(name) := src!.(name);
  od;
  return dst;
end);

#############################################################################
##
#O  GenerateID( ) . . . . . . . . . . . Generates sequential ids for objects
##
InstallMethod(GenerateID,
  "",
  true,
  [],
  0,
function()
  FrancyGeneratedID := FrancyGeneratedID + 1;
  return Concatenation("F", String(FrancyGeneratedID));
end);


--/**
-- * <copyright>
-- *
-- * Copyright (c) 2008, 2009 Eclipse.org and others.
-- * All rights reserved.   This program and the accompanying materials
-- * are made available under the terms of the Eclipse Public License v2.0
-- * which accompanies this distribution, and is available at
-- * http://www.eclipse.org/legal/epl-v20.html
-- *
-- * Contributors:
-- *   E.D.Willink - Initial API and implementation
-- *   Adolfo Sanchez-Barbudo Herrera (Open Canarias):
-- *        - 242153: LPG v 2.0.17 adoption.
-- *        - 299396: Introducing new LPG templates.
-- *        - 300534: Removing the use of deprecated macros.
-- *
-- * </copyright>
-- */
--
-- Additional ERROR_TOKEN rules for The EssentialOCL Backtracking Parser
--

%Headers
	/.
	// Some methods for backwards compatibility

	/**
	 * Report error message for given error_token.
	 *
	 * @param error_token
	 *            the error taken index
	 * @param msg
	 *            the message to report
	 *
	 * @since 1.3
	 */
	protected final void reportErrorTokenMessage(int error_token, String msg) {
		getIPrsStream().reportErrorTokenMessage(error_token, msg);
	}
	./
%End

%Rules
	ERROR_Colon ::= ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(1), OCLParserErrors.MISSING_COLON);
		  $EndCode
		./
	ERROR_Empty ::= ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(1), OCLParserErrors.EXTRA_TOKENS);
		  $EndCode
		./

-----------------------------------------------------------------------
--	Names
-----------------------------------------------------------------------

	ERROR_SimpleNameCS ::= ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(1), OCLParserErrors.MISSING_SIMPLE_NAME);
                    IToken iToken = getRhsIToken(1);
					SimpleNameCS result = createSimpleNameCS(
							SimpleTypeEnum.IDENTIFIER_LITERAL,
							iToken
						);
					setOffsets(result, iToken);
					setResult(result);
		  $EndCode
		./

	simpleNameCS -> ERROR_SimpleNameCS
--	simpleNameCS -> reservedKeyword ERROR_SimpleNameCS

-----------------------------------------------------------------------
--	Types
-----------------------------------------------------------------------
	collectionTypeCS ::= CollectionTypeIdentifierCS '(' typeCS ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(4), OCLParserErrors.MISSING_RPAREN);
					CollectionTypeCS result = (CollectionTypeCS)getRhsSym(1);
					result.setTypeCS((TypeCS)getRhsSym(3));
					setOffsets(result, result, getRhsIToken(4));
					setResult(result);
		  $EndCode
		./

-----------------------------------------------------------------------
--	Declarations
-----------------------------------------------------------------------
--	VariableDeclarationCS ::= notLiteralNorReservedSimpleNameCS ERROR_TOKEN
--		/.$BeginCode
--					reportErrorTokenMessage(getRhsTokenIndex(2), OCLParserErrors.MISSING_VARIABLE_TYPE);
--					SimpleNameCS name = (SimpleNameCS)getRhsSym(1);
--					VariableCS result = createVariableCS(name, null, null);
--					setOffsets(result, name, getRhsIToken(2));
--					setResult(result);
--		  $EndCode
--		./

--	variableDeclarationListCS ::= ERROR_TOKEN
--		/.$BeginCode
--					reportErrorTokenMessage(getRhsTokenIndex(1), OCLParserErrors.MISSING_VARIABLES);
--					EList result = new BasicEList();
--					setResult(result);
--		  $EndCode
--		./

-----------------------------------------------------------------------
--	Literals
-----------------------------------------------------------------------
	TupleLiteralExpCS ::= Tuple ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(2), OCLParserErrors.MISSING_LBRACE);
					TupleLiteralExpCS result = createTupleLiteralExpCS((EList<VariableCS>)getRhsSym(3));
					setOffsets(result, getRhsIToken(1), getRhsIToken(4));
					setResult(result);
		  $EndCode
		./

	TupleLiteralPartsCS ::= ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(1), OCLParserErrors.MISSING_VARIABLES);
					EList<VariableCS> result = new BasicEList<VariableCS>();
					setResult(result);
		  $EndCode
		./

-----------------------------------------------------------------------
--	Calls
-----------------------------------------------------------------------
	AssociationClassCallExpCS ::= simpleNameCS '[' argumentsCS ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(4), OCLParserErrors.MISSING_RBRACK);
					VariableExpCS result = createVariableExpCS(
							(SimpleNameCS)getRhsSym(1),
							(EList<OCLExpressionCS>)getRhsSym(3),
							null
						);
					setOffsets(result, (CSTNode)getRhsSym(1), getRhsIToken(4));
					setResult(result);
		  $EndCode
		./

-----------------------------------------------------------------------
--	Expressions
-----------------------------------------------------------------------
	IfExpCS ::= if OclExpressionCS then OclExpressionCS else OclExpressionCS ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(7), OCLParserErrors.MISSING_ENDIF);
					IfExpCS result = createIfExpCS(
							(OCLExpressionCS)getRhsSym(2),
							(OCLExpressionCS)getRhsSym(4),
							(OCLExpressionCS)getRhsSym(6)
						);
					setOffsets(result, getRhsIToken(1), getRhsIToken(7));
					setResult(result);
		  $EndCode
		./
	IfExpCS ::= if OclExpressionCS then OclExpressionCS ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(5), OCLParserErrors.MISSING_ELSE_ENDIF);
					IfExpCS result = createIfExpCS(
							(OCLExpressionCS)getRhsSym(2),
							(OCLExpressionCS)getRhsSym(4),
							createInvalidLiteralExpCS(getRhsTokenText(5))
						);
					setOffsets(result, getRhsIToken(1), getRhsIToken(5));
					setResult(result);
		  $EndCode
		./
	IfExpCS ::= if OclExpressionCS ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(3), OCLParserErrors.MISSING_THEN_ELSE_ENDIF);
					IfExpCS result = createIfExpCS(
							(OCLExpressionCS)getRhsSym(2),
							createInvalidLiteralExpCS(getRhsTokenText(3)),
							createInvalidLiteralExpCS(getRhsTokenText(3))
						);
					setOffsets(result, getRhsIToken(1), getRhsIToken(3));
					setResult(result);
		  $EndCode
		./
	IfExpCS ::= if ERROR_TOKEN endif
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(3), OCLParserErrors.MISSING_THEN_ELSE);
					IfExpCS result = createIfExpCS(
							createInvalidLiteralExpCS(getRhsTokenText(2)),
							createInvalidLiteralExpCS(getRhsTokenText(2)),
							createInvalidLiteralExpCS(getRhsTokenText(2))
						);
					setOffsets(result, getRhsIToken(1), getRhsIToken(3));
					setResult(result);
		  $EndCode
		./

	primaryExpCS ::= '(' OclExpressionCS ERROR_TOKEN
		/.$BeginCode
					reportErrorTokenMessage(getRhsTokenIndex(3), OCLParserErrors.MISSING_RPAREN);
					OCLExpressionCS result = (OCLExpressionCS)getRhsSym(2);
					setOffsets(result, getRhsIToken(1), getRhsIToken(3));
					setResult(result);
		  $EndCode
		./
%End