func builtinDefmacro(context *runtime.MacroCallContext) (*runtime.Value, error) { name := context.Nodes[0].(*parser.IdentifierNode).Token.Data if name == "_" { return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "disallowed macro name") } if context.Block.Scope.GetSymbol(name) != nil && context.Block.Scope.GetSymbol(name).Const { return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "%s is a constant and cannot be modified", name) } argNodes := context.Nodes[1].(*parser.ListNode) var args []string callback := context.Nodes[2].(*parser.ListNode) if len(callback.Nodes) == 0 { return nil, runtime.NewRuntimeError(callback.Pos(), "empty macro body") } for _, argNode := range argNodes.Nodes { ident, ok := argNode.(*parser.IdentifierNode) if !ok { return nil, runtime.NewRuntimeError(argNode.Pos(), "expected an identifier") } args = append(args, ident.Token.Data) } macro := runtime.NewMacro(func(handlerContext *runtime.MacroCallContext) (*runtime.Value, error) { block := runtime.NewBlock([]parser.Node{callback}, runtime.NewScope(handlerContext.Block.Scope)) for i, arg := range args { block.Scope.SetSymbolLocally(arg, runtime.NewSymbol(runtime.NewQuotedValue(handlerContext.Nodes[i]))) } return block.Eval() }, false) context.Block.Scope.SetMacro(name, macro) return runtime.Nil, nil }
package builtin import ( "github.com/raoulvdberge/risp/parser" "github.com/raoulvdberge/risp/runtime" ) var Macros = runtime.Mactab{ "defun": runtime.NewMacro(builtinDefun, true, "identifier", "list", "list"), "def": runtime.NewMacro(builtinDef, true, "identifier", "any"), "defmacro": runtime.NewMacro(builtinDefmacro, true, "identifier", "list", "list"), "defconst": runtime.NewMacro(builtinDef, true, "identifier", "any"), "fun": runtime.NewMacro(builtinFun, true, "list", "list"), "for": runtime.NewMacro(builtinFor, true, "any", "list", "list"), "while": runtime.NewMacro(builtinWhile, true, "any", "list"), "if": runtime.NewMacro(builtinIf, true, "any", "any"), "ifel": runtime.NewMacro(builtinIfel, true, "any", "any", "any"), "case": runtime.NewMacro(builtinCase, false), "export": runtime.NewMacro(builtinExport, false), "namespace": runtime.NewMacro(builtinNamespace, true, "identifier"), } func builtinDefmacro(context *runtime.MacroCallContext) (*runtime.Value, error) { name := context.Nodes[0].(*parser.IdentifierNode).Token.Data if name == "_" { return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "disallowed macro name") } if context.Block.Scope.GetSymbol(name) != nil && context.Block.Scope.GetSymbol(name).Const { return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "%s is a constant and cannot be modified", name)
package list import ( "github.com/raoulvdberge/risp/parser" "github.com/raoulvdberge/risp/runtime" ) var Macros = runtime.Mactab{ "filter": runtime.NewMacro(listFilter, true, "any", "identifier", "list"), "map": runtime.NewMacro(listMap, true, "any", "identifier", "list"), "reduce": runtime.NewMacro(listReduce, true, "any", "identifier", "identifier", "list"), } func listFilter(context *runtime.MacroCallContext) (*runtime.Value, error) { list, err := context.Block.EvalNode(context.Nodes[0]) if err != nil { return nil, err } if list.Type != runtime.ListValue { return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "expected a list") } ident := context.Nodes[1].(*parser.IdentifierNode).Token.Data callback := context.Nodes[2] filteredList := runtime.NewListValue() for _, item := range list.List {