func run(source lexer.Source) { l := lexer.NewLexer(source) util.Timed("lexing", *debug, func() { util.ReportError(l.Lex(), false) }) p := parser.NewParser(l.Tokens) util.Timed("parsing", *debug, func() { util.ReportError(p.Parse(), false) }) if *ast { bytes, _ := json.MarshalIndent(p, "", " ") fmt.Println(string(bytes)) } else { b := apply(runtime.NewBlock(p.Nodes, runtime.NewScope(nil))) util.Timed("runtime", *debug, func() { _, err := b.Eval() if err != nil { util.ReportError(err, false) } }) } }
func main() { flag.Usage = usage flag.Parse() if len(flag.Args()) > 0 { var file *util.File util.Timed("file reading", *debug, func() { os.Chdir(filepath.Dir(flag.Arg(0))) f, err := util.NewFile(filepath.Base(flag.Arg(0))) if err != nil { util.ReportError(err, false) } file = f }) run(lexer.NewSourceFromFile(file)) } else if *runRepl { s := repl.NewReplSession(apply(runtime.NewBlock(nil, runtime.NewScope(nil)))) s.Run() } else { bytes, err := ioutil.ReadAll(os.Stdin) if err != nil { util.ReportError(err, false) } run(lexer.NewSourceFromString("<stdin>", string(bytes))) } }
func listMap(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] mappedList := runtime.NewListValue() for _, item := range list.List { b := runtime.NewBlock([]parser.Node{callback}, runtime.NewScope(context.Block.Scope)) b.Scope.SetSymbolLocally(ident, runtime.NewSymbol(item)) result, err := b.Eval() if err != nil { return nil, err } mappedList.List = append(mappedList.List, result) } return mappedList, nil }
func builtinFor(context *runtime.MacroCallContext) (*runtime.Value, error) { l, err := context.Block.EvalNode(context.Nodes[0]) if err != nil { return nil, err } if l.Type != runtime.ListValue { return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "expected a list to iterate over") } var args []string for _, nameNode := range context.Nodes[1].(*parser.ListNode).Nodes { ident, isIdent := nameNode.(*parser.IdentifierNode) if isIdent { args = append(args, ident.Token.Data) } else { return nil, runtime.NewRuntimeError(nameNode.Pos(), "expected an identifier") } } if len(args) > 2 { return nil, runtime.NewRuntimeError(context.Nodes[1].Pos(), "too many arguments provided") } callbackBlock := runtime.NewBlock([]parser.Node{context.Nodes[2]}, runtime.NewScope(context.Block.Scope)) for i, item := range l.List { if len(args) >= 1 { callbackBlock.Scope.SetSymbol(args[0], runtime.NewSymbol(item)) } if len(args) == 2 { callbackBlock.Scope.SetSymbol(args[1], runtime.NewSymbol(runtime.NewNumberValueFromInt64(int64(i)))) } _, err := callbackBlock.Eval() if err != nil { return nil, err } } return runtime.Nil, nil }
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 }
func listReduce(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") } if len(list.List) == 0 { return nil, runtime.NewRuntimeError(context.Nodes[0].Pos(), "empty list") } identLeft := context.Nodes[1].(*parser.IdentifierNode).Token.Data identRight := context.Nodes[2].(*parser.IdentifierNode).Token.Data callback := context.Nodes[3] reduced := list.List[0] for _, item := range list.List[1:] { b := runtime.NewBlock([]parser.Node{callback}, runtime.NewScope(context.Block.Scope)) b.Scope.SetSymbolLocally(identLeft, runtime.NewSymbol(reduced)) b.Scope.SetSymbolLocally(identRight, runtime.NewSymbol(item)) result, err := b.Eval() if err != nil { return nil, err } reduced = result } return reduced, nil }
func builtinLoad(context *runtime.FunctionCallContext) (*runtime.Value, error) { if err := runtime.ValidateArguments(context, runtime.StringValue); err != nil { return nil, err } file, err := util.NewFile(context.Args[0].Str) if err != nil { return nil, err } l := lexer.NewLexer(lexer.NewSourceFromFile(file)) util.ReportError(l.Lex(), false) p := parser.NewParser(l.Tokens) util.ReportError(p.Parse(), false) b := runtime.NewBlock(p.Nodes, runtime.NewScope(context.Block.Scope)) result, err := b.Eval() if err != nil { return nil, err } for key, value := range b.Scope.Symbols { if value.Exported { if value.Value.Type == runtime.FunctionValue { value.Value.Function.CustomScope = b.Scope } context.Block.Scope.SetSymbol(b.SymbolName(key), value) } } return result, nil }