/// <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 }