123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705 |
- package glisp
- import (
- "bytes"
- "errors"
- "fmt"
- "os"
- )
- var WrongNargs error = errors.New("wrong number of arguments")
- type GlispFunction []Instruction
- type GlispUserFunction func(*Glisp, string, []Sexp) (Sexp, error)
- func CompareFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- res, err := Compare(args[0], args[1])
- if err != nil {
- return SexpNull, err
- }
- cond := false
- switch name {
- case "<":
- cond = res < 0
- case ">":
- cond = res > 0
- case "<=":
- cond = res <= 0
- case ">=":
- cond = res >= 0
- case "=":
- cond = res == 0
- case "not=":
- cond = res != 0
- }
- return SexpBool(cond), nil
- }
- func BinaryIntFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- var op IntegerOp
- switch name {
- case "sll":
- op = ShiftLeft
- case "sra":
- op = ShiftRightArith
- case "srl":
- op = ShiftRightLog
- case "mod":
- op = Modulo
- }
- return IntegerDo(op, args[0], args[1])
- }
- func BitwiseFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- var op IntegerOp
- switch name {
- case "bit-and":
- op = BitAnd
- case "bit-or":
- op = BitOr
- case "bit-xor":
- op = BitXor
- }
- accum := args[0]
- var err error
- for _, expr := range args[1:] {
- accum, err = IntegerDo(op, accum, expr)
- if err != nil {
- return SexpNull, err
- }
- }
- return accum, nil
- }
- func ComplementFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- switch t := args[0].(type) {
- case SexpInt:
- return ^t, nil
- case SexpChar:
- return ^t, nil
- }
- return SexpNull, errors.New("Argument to bit-not should be integer")
- }
- func NumericFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) < 1 {
- return SexpNull, WrongNargs
- }
- var err error
- accum := args[0]
- var op NumericOp
- switch name {
- case "+":
- op = Add
- case "-":
- op = Sub
- case "*":
- op = Mult
- case "/":
- op = Div
- }
- for _, expr := range args[1:] {
- accum, err = NumericDo(op, accum, expr)
- if err != nil {
- return SexpNull, err
- }
- }
- return accum, nil
- }
- func ConsFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- return Cons(args[0], args[1]), nil
- }
- func FirstFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- switch expr := args[0].(type) {
- case SexpPair:
- return expr.head, nil
- case SexpArray:
- return expr[0], nil
- }
- return SexpNull, WrongType
- }
- func RestFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- switch expr := args[0].(type) {
- case SexpPair:
- return expr.tail, nil
- case SexpArray:
- if len(expr) == 0 {
- return expr, nil
- }
- return expr[1:], nil
- case SexpSentinel:
- if expr == SexpNull {
- return SexpNull, nil
- }
- }
- return SexpNull, WrongType
- }
- func ArrayAccessFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) < 2 {
- return SexpNull, WrongNargs
- }
- var arr SexpArray
- switch t := args[0].(type) {
- case SexpArray:
- arr = t
- default:
- return SexpNull, errors.New("First argument of aget must be array")
- }
- var i int
- switch t := args[1].(type) {
- case SexpInt:
- i = int(t)
- case SexpChar:
- i = int(t)
- default:
- return SexpNull, errors.New("Second argument of aget must be integer")
- }
- if i < 0 || i >= len(arr) {
- return SexpNull, errors.New("Array index out of bounds")
- }
- if name == "aget" {
- return arr[i], nil
- }
- if len(args) != 3 {
- return SexpNull, WrongNargs
- }
- arr[i] = args[2]
- return SexpNull, nil
- }
- func SgetFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- var str SexpStr
- switch t := args[0].(type) {
- case SexpStr:
- str = t
- default:
- return SexpNull, errors.New("First argument of sget must be string")
- }
- var i int
- switch t := args[1].(type) {
- case SexpInt:
- i = int(t)
- case SexpChar:
- i = int(t)
- default:
- return SexpNull, errors.New("Second argument of sget must be integer")
- }
- return SexpChar(str[i]), nil
- }
- func HashAccessFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) < 2 || len(args) > 3 {
- return SexpNull, WrongNargs
- }
- var hash SexpHash
- switch e := args[0].(type) {
- case SexpHash:
- hash = e
- default:
- return SexpNull, errors.New("first argument of hget must be hash")
- }
- switch name {
- case "hget":
- if len(args) == 3 {
- return hash.HashGetDefault(args[1], args[2])
- }
- return hash.HashGet(args[1])
- case "hset!":
- err := hash.HashSet(args[1], args[2])
- return SexpNull, err
- case "hdel!":
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- err := hash.HashDelete(args[1])
- return SexpNull, err
- }
- return SexpNull, nil
- }
- func SliceFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 3 {
- return SexpNull, WrongNargs
- }
- var start int
- var end int
- switch t := args[1].(type) {
- case SexpInt:
- start = int(t)
- case SexpChar:
- start = int(t)
- default:
- return SexpNull, errors.New("Second argument of slice must be integer")
- }
- switch t := args[2].(type) {
- case SexpInt:
- end = int(t)
- case SexpChar:
- end = int(t)
- default:
- return SexpNull, errors.New("Third argument of slice must be integer")
- }
- switch t := args[0].(type) {
- case SexpArray:
- return SexpArray(t[start:end]), nil
- case SexpStr:
- return SexpStr(t[start:end]), nil
- }
- return SexpNull, errors.New("First argument of slice must be array or string")
- }
- func LenFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- switch t := args[0].(type) {
- case SexpArray:
- return SexpInt(len(t)), nil
- case SexpStr:
- return SexpInt(len(t)), nil
- case SexpHash:
- return SexpInt(HashCountKeys(t)), nil
- }
- return SexpInt(0), errors.New("argument must be string or array")
- }
- func AppendFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- switch t := args[0].(type) {
- case SexpArray:
- return SexpArray(append(t, args[1])), nil
- case SexpStr:
- return AppendStr(t, args[1])
- }
- return SexpNull, errors.New("First argument of append must be array or string")
- }
- func ConcatFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- switch t := args[0].(type) {
- case SexpArray:
- return ConcatArray(t, args[1])
- case SexpStr:
- return ConcatStr(t, args[1])
- case SexpPair:
- return ConcatList(t, args[1])
- }
- return SexpNull, errors.New("expected strings or arrays")
- }
- func ReadFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- str := ""
- switch t := args[0].(type) {
- case SexpStr:
- str = string(t)
- default:
- return SexpNull, WrongType
- }
- lexer := NewLexerFromStream(bytes.NewBuffer([]byte(str)))
- parser := Parser{lexer, env}
- return ParseExpression(&parser)
- }
- func EvalFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- newenv := env.Duplicate()
- err := newenv.LoadExpressions(args)
- if err != nil {
- return SexpNull, errors.New("failed to compile expression")
- }
- newenv.pc = 0
- return newenv.Run()
- }
- func TypeQueryFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- var result bool
- switch name {
- case "list?":
- result = IsList(args[0])
- case "null?":
- result = (args[0] == SexpNull)
- case "array?":
- result = IsArray(args[0])
- case "number?":
- result = IsNumber(args[0])
- case "float?":
- result = IsFloat(args[0])
- case "int?":
- result = IsInt(args[0])
- case "char?":
- result = IsChar(args[0])
- case "symbol?":
- result = IsSymbol(args[0])
- case "string?":
- result = IsString(args[0])
- case "hash?":
- result = IsHash(args[0])
- case "zero?":
- result = IsZero(args[0])
- case "empty?":
- result = IsEmpty(args[0])
- }
- return SexpBool(result), nil
- }
- func PrintFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- var str string
- switch expr := args[0].(type) {
- case SexpStr:
- str = string(expr)
- default:
- str = expr.SexpString()
- }
- switch name {
- case "println":
- fmt.Println(str)
- case "print":
- fmt.Print(str)
- }
- return SexpNull, nil
- }
- func NotFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- result := SexpBool(!IsTruthy(args[0]))
- return result, nil
- }
- func ApplyFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- var fun SexpFunction
- var funargs SexpArray
- switch e := args[0].(type) {
- case SexpFunction:
- fun = e
- default:
- return SexpNull, errors.New("first argument must be function")
- }
- switch e := args[1].(type) {
- case SexpArray:
- funargs = e
- case SexpPair:
- var err error
- funargs, err = ListToArray(e)
- if err != nil {
- return SexpNull, err
- }
- default:
- return SexpNull, errors.New("second argument must be array or list")
- }
- return env.Apply(fun, funargs)
- }
- func MapFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 2 {
- return SexpNull, WrongNargs
- }
- var fun SexpFunction
- switch e := args[0].(type) {
- case SexpFunction:
- fun = e
- default:
- return SexpNull, errors.New(fmt.Sprint("first argument must be function had", fmt.Sprintf("%T", e), " ", e))
- }
- switch e := args[1].(type) {
- case SexpArray:
- return MapArray(env, fun, e)
- case SexpPair:
- return MapList(env, fun, e)
- }
- return SexpNull, errors.New("second argument must be array")
- }
- func MakeArrayFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) < 1 {
- return SexpNull, WrongNargs
- }
- var size int
- switch e := args[0].(type) {
- case SexpInt:
- size = int(e)
- default:
- return SexpNull, errors.New("first argument must be integer")
- }
- var fill Sexp
- if len(args) == 2 {
- fill = args[1]
- } else {
- fill = SexpNull
- }
- arr := make([]Sexp, size)
- for i := range arr {
- arr[i] = fill
- }
- return SexpArray(arr), nil
- }
- func ConstructorFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- switch name {
- case "array":
- return SexpArray(args), nil
- case "list":
- return MakeList(args), nil
- case "hash":
- return MakeHash(args, "hash")
- }
- return SexpNull, errors.New("invalid constructor")
- }
- func SymnumFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- switch t := args[0].(type) {
- case SexpSymbol:
- return SexpInt(t.number), nil
- }
- return SexpNull, errors.New("argument must be symbol")
- }
- func SourceFileFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) < 1 {
- return SexpNull, WrongNargs
- }
- var sourceItem func(item Sexp) error
- sourceItem = func(item Sexp) error {
- switch t := item.(type) {
- case SexpArray:
- for _, v := range t {
- if err := sourceItem(v); err != nil {
- return err
- }
- }
- case SexpPair:
- expr := item
- for expr != SexpNull {
- list := expr.(SexpPair)
- if err := sourceItem(list.head); err != nil {
- return err
- }
- expr = list.tail
- }
- case SexpStr:
- var f *os.File
- var err error
- if f, err = os.Open(string(t)); err != nil {
- return err
- }
- if err = env.SourceFile(f); err != nil {
- return err
- }
- f.Close()
- default:
- return fmt.Errorf("%v: Expected `string`, `list`, `array` given type %T val %v", name, item, item)
- }
- return nil
- }
- for _, v := range args {
- if err := sourceItem(v); err != nil {
- return SexpNull, err
- }
- }
- return SexpNull, nil
- }
- var MissingFunction = SexpFunction{"__missing", true, 0, false, nil, nil, nil}
- func MakeFunction(name string, nargs int, varargs bool,
- fun GlispFunction) SexpFunction {
- var sfun SexpFunction
- sfun.name = name
- sfun.user = false
- sfun.nargs = nargs
- sfun.varargs = varargs
- sfun.fun = fun
- return sfun
- }
- func MakeUserFunction(name string, ufun GlispUserFunction) SexpFunction {
- var sfun SexpFunction
- sfun.name = name
- sfun.user = true
- sfun.userfun = ufun
- return sfun
- }
- var BuiltinFunctions = map[string]GlispUserFunction{
- "<": CompareFunction,
- ">": CompareFunction,
- "<=": CompareFunction,
- ">=": CompareFunction,
- "=": CompareFunction,
- "not=": CompareFunction,
- "sll": BinaryIntFunction,
- "sra": BinaryIntFunction,
- "srl": BinaryIntFunction,
- "mod": BinaryIntFunction,
- "+": NumericFunction,
- "-": NumericFunction,
- "*": NumericFunction,
- "/": NumericFunction,
- "bit-and": BitwiseFunction,
- "bit-or": BitwiseFunction,
- "bit-xor": BitwiseFunction,
- "bit-not": ComplementFunction,
- "read": ReadFunction,
- "cons": ConsFunction,
- "first": FirstFunction,
- "rest": RestFunction,
- "car": FirstFunction,
- "cdr": RestFunction,
- "list?": TypeQueryFunction,
- "null?": TypeQueryFunction,
- "array?": TypeQueryFunction,
- "hash?": TypeQueryFunction,
- "number?": TypeQueryFunction,
- "int?": TypeQueryFunction,
- "float?": TypeQueryFunction,
- "char?": TypeQueryFunction,
- "symbol?": TypeQueryFunction,
- "string?": TypeQueryFunction,
- "zero?": TypeQueryFunction,
- "empty?": TypeQueryFunction,
- "println": PrintFunction,
- "print": PrintFunction,
- "not": NotFunction,
- "apply": ApplyFunction,
- "map": MapFunction,
- "make-array": MakeArrayFunction,
- "aget": ArrayAccessFunction,
- "aset!": ArrayAccessFunction,
- "sget": SgetFunction,
- "hget": HashAccessFunction,
- "hset!": HashAccessFunction,
- "hdel!": HashAccessFunction,
- "slice": SliceFunction,
- "len": LenFunction,
- "append": AppendFunction,
- "concat": ConcatFunction,
- "array": ConstructorFunction,
- "list": ConstructorFunction,
- "hash": ConstructorFunction,
- "symnum": SymnumFunction,
- "str": StringifyFunction,
- }
- func StringifyFunction(env *Glisp, name string, args []Sexp) (Sexp, error) {
- if len(args) != 1 {
- return SexpNull, WrongNargs
- }
- return SexpStr(args[0].SexpString()), nil
- }
|