/* SHS: Syntactically Homogeneous Shell * Copyright (C) 2019 Aidan Hahn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package stdlib import ( "gitlab.com/whom/shs/ast" "gitlab.com/whom/shs/log" ) /* Takes 3 arguments: name, list of arg names, and logic form * DOES NOT EVALUATE THE LOGIC FORM * adds an anonymous function to the FuncTable under the name specified * anonymous function will expect the args declared in arg2 and expand them in arg3 * Then evaluates and returns result of arg3. This constitutes a function call * * Example: * (func foo (x) (print x)) * (foo 4) -> prints 4 */ func DeclFunc(input *ast.Token, vars ast.VarTable, funcs ast.FuncTable) *ast.Token { name := input if name.Tag != ast.SYMBOL { log.Log(log.ERR, "argument 1 of func must be a symbol to be exported", "func") return nil } var numArgs int args := name.Next if args.Tag != ast.LIST { log.Log(log.ERR, "argument 2 of func must be a flat list of argument symbols", "func") return nil } form := args.Next if form.Tag != ast.LIST { log.Log(log.ERR, "argument 3 of func must be a form to be evaluated", "func") return nil } for i := args.Expand(); i != nil; i = i.Next { if i.Tag != ast.SYMBOL { log.Log(log.ERR, "all args in user defined functions must be declared in the form of symbols", "func") return nil } numArgs += 1 } ASTSYNCSTATE := ast.SyncTablesWithOSEnviron inner := func(in *ast.Token, vt ast.VarTable, ft ast.FuncTable) *ast.Token { var temp *ast.Token if numArgs != 0 || in != nil { temp = in.Eval(ft, vt, false) if temp == nil { log.Log(log.ERR, "error parsing arguments", name.Value()) return nil } ast.SyncTablesWithOSEnviron = false key_iter := args.Expand() val_iter := temp for key_iter != nil { if val_iter == nil { log.Log(log.ERR, "Not enough arguments supplied", name.Value()) } ast.SetVar(key_iter.Value(), val_iter, vt) key_iter = key_iter.Next val_iter = val_iter.Next } } // maybe we actually should put the inner scope var into the env ast.SyncTablesWithOSEnviron = ASTSYNCSTATE ret := form.Eval(ft, vt, false) ast.SyncTablesWithOSEnviron = false for i := args.Expand(); i != nil; i = i.Next { ast.RemoveVar(i.Value(), vt) } ast.SyncTablesWithOSEnviron = ASTSYNCSTATE return ret } (*funcs)[name.Value()] = &ast.Function{ Function: inner, Name: name.Value(), TimesCalled: 0, NumArgs: numArgs, ArgLazy: true, } return nil }