Example #1
0
        /// <summary>
        /// Compile a custom function definition
        /// </summary>
        private static void CompileFunctionDefinition(int level, bool debug, Node node, NanCodeWriter wr, Scope parameterNames)
        {
            // 1) Compile the func to a temporary string
            // 2) Inject a new 'def' op-code, that names the function and does an unconditional jump over it.
            // 3) Inject the compiled func
            // 4) Inject a new 'return' op-code

            if (node.Children.Count != 2)
            {
                throw new Exception("Function definition must have 3 parts: the name, the parameter list, and the definition.\r\n" +
                                    "Call like `def (   myFunc ( param1 param2 ) ( ... statements ... )   )`");
            }

            var bodyNode       = node.Children.Last.Value;
            var definitionNode = node.Children.First.Value;

            if (definitionNode.Children.Any(c => c.IsLeaf == false))
            {
                throw new Exception("Function parameters must be simple names.\r\n" +
                                    "`def ( myFunc (  param1  ) ( ... ) )` is OK,\r\n" +
                                    "`def ( myFunc ( (param1) ) ( ... ) )` is not OK");
            }
            if (bodyNode.Text != "()")
            {
                throw new Exception("Bare functions not supported. Wrap your function body in (parenthesis)");
            }

            var functionName = definitionNode.Text;
            var argCount     = definitionNode.Children.Count;

            ParameterPositions(parameterNames, definitionNode.Children.Select(c => c.Text), wr);

            var subroutine = Compile(bodyNode, level, debug, parameterNames, null, Context.Default);
            var tokenCount = subroutine.OpCodeCount();

            if (debug)
            {
                wr.Comment("// Function definition : \"" + functionName
                           + "\" with " + argCount + " parameter(s)");
            }

            wr.FunctionDefine(functionName, argCount, tokenCount);
            wr.Merge(subroutine);

            if (subroutine.ReturnsValues)
            {
                // Add an invalid return opcode. This will show an error message.
                wr.InvalidReturn();
            }
            else
            {
                // Add the 'return' call
                wr.Return(0);
            }

            // Then the runner will need to interpret both the new op-codes
            // This would include a return-stack-push for calling functions,
            //   a jump-to-absolute-position by name
            //   a jump-to-absolute-position by return stack & pop
        }