namespace KumiScript.Interpreter { public class LispPrimitives { public static Expression Lambda(ListExpression args, Environment env) { Expression car = args.Car(); ListExpression rest = args.Cdr(); ListExpression? head = car as ListExpression; if (head is null) throw new InterpreterInvalidDefinitionException(); List headl = head.GetMembers(); List arg_names = new List(); for (int i = 0; i < headl.Count; i++) { SymbolExpression? argi = headl[i] as SymbolExpression; if (argi is null) throw new InterpreterInvalidDefinitionException(); arg_names.Add(argi.GetSymbol()); } Expression body = rest.Car(); return new ProcedureExpression(new LispProcedure(arg_names, body, env)); } public static NumberExpression Plus(ListExpression args, Environment env) { List argl = args.EvalMembers(env); decimal acc = 0; for (int i = 0; i < argl.Count; i++) { NumberExpression? argin = argl[i] as NumberExpression; if (argin is null) throw new InterpreterTypingException(); acc += argin.GetValueAsFloat(); } return NumberFactory.NormalizeFloat(acc); } public static NumberExpression Minus(ListExpression args, Environment env) { List argl = args.EvalMembers(env); if (argl.Count == 0) throw new InterpreterInvalidApplicationException(); NumberExpression? argn0 = argl[0] as NumberExpression; if (argn0 is null) throw new InterpreterTypingException(); decimal acc = argn0.GetValueAsFloat(); if (argl.Count == 1) return NumberFactory.NormalizeFloat(1 - acc); for (int i = 1; i < argl.Count; i++) { NumberExpression? argin = argl[i] as NumberExpression; if (argin is null) throw new InterpreterTypingException(); acc -= argin.GetValueAsFloat(); } return NumberFactory.NormalizeFloat(acc); } public static NumberExpression Times(ListExpression args, Environment env) { List argl = args.EvalMembers(env); if (argl.Count < 2) throw new InterpreterInvalidInvocationException(); NumberExpression? arg0 = argl[0] as NumberExpression; if (arg0 is null) throw new InterpreterTypingException(); decimal f = arg0.GetValueAsFloat(); for (int i = 1; i < argl.Count; i++) { NumberExpression? argn = argl[i] as NumberExpression; if (argn is null) throw new InterpreterTypingException(); f *= argn.GetValueAsFloat(); } return NumberFactory.NormalizeFloat(f); } public static NumberExpression DividedBy(ListExpression args, Environment env) { List argl = args.EvalMembers(env); if (argl.Count != 2) throw new InterpreterInvalidApplicationException(); NumberExpression? arg0 = argl[0] as NumberExpression; if (arg0 is null) throw new InterpreterTypingException(); NumberExpression? arg1 = argl[1] as NumberExpression; if (arg1 is null) throw new InterpreterTypingException(); return NumberFactory.NormalizeFloat(arg0.GetValueAsFloat() / arg1.GetValueAsFloat()); } public static NumberExpression Modulus(ListExpression args, Environment env) { List argl = args.EvalMembers(env); if (argl.Count != 2) throw new InterpreterInvalidApplicationException(); NumberExpression? arg0 = argl[0] as NumberExpression; if (arg0 is null) throw new InterpreterTypingException(); NumberExpression? arg1 = argl[1] as NumberExpression; if (arg1 is null) throw new InterpreterTypingException(); return NumberFactory.NormalizeFloat(arg0.GetValueAsFloat() % arg1.GetValueAsFloat()); } public static Expression Quote(ListExpression args, Environment env) { List argl = args.GetMembers(); if (argl.Count != 1) throw new InterpreterInvalidApplicationException(); return argl[0]; } public static Expression Define(ListExpression args, Environment env) { List argl = args.GetMembers(); if (argl.Count != 2) throw new InterpreterInvalidApplicationException(); SymbolExpression? binding = argl[0] as SymbolExpression; if (binding is null) throw new InterpreterTypingException(); Symbol name = binding.GetSymbol(); Expression definition = argl[1].Eval(env); env.AddSymbol(name, definition); return definition; } public static Expression Car(ListExpression args, Environment env) { List argl = args.EvalMembers(env); if (argl.Count != 1) throw new InterpreterInvalidApplicationException(); ListExpression? list = argl[0] as ListExpression; if (list is null) throw new InterpreterTypingException(); return list.Car(); } public static ListExpression Cdr(ListExpression args, Environment env) { List argl = args.EvalMembers(env); if (argl.Count != 1) throw new InterpreterInvalidApplicationException(); ListExpression? list = argl[0] as ListExpression; if (list is null) throw new InterpreterTypingException(); return list.Cdr(); } public static ProperListExpression Cons(ListExpression args, Environment env) { List argl = args.EvalMembers(env); if (argl.Count != 2) throw new InterpreterInvalidApplicationException(); Expression car = argl[0]; ListExpression? cdr = argl[1] as ListExpression; if (cdr is null) throw new InterpreterTypingException(); return cdr.Cons(car); } public static Expression Quit(ListExpression args, Environment env) { System.Environment.Exit(0); return new SymbolExpression(new Symbol("bye")); } public static Expression Cond(ListExpression args, Environment env) { List argl = args.GetMembers(); for (int i = 0; i < argl.Count; i ++) { ProperListExpression? conditional = argl[i] as ProperListExpression; if (conditional is null) throw new InterpreterTypingException(); Expression antecedent = conditional.Car(); Expression consequent = conditional.Cdr().Car(); if (!(antecedent.Eval(env) is NilExpression)) return consequent.Eval(env); } return NilExpression.GetInstance(); } public static Expression Eq(ListExpression args, Environment env) { List argl = args.EvalMembers(env); if (argl.Count != 2) throw new InterpreterInvalidApplicationException(); if (argl[0].Equals(argl[1])) return TrueExpression.GetInstance(); return NilExpression.GetInstance(); } public static Expression Set(ListExpression args, Environment env) { List argl = args.GetMembers(); if (argl.Count != 2) throw new InterpreterInvalidApplicationException(); SymbolExpression? binding = argl[0] as SymbolExpression; if (binding is null) throw new InterpreterTypingException(); Symbol name = binding.GetSymbol(); Expression definition = argl[1].Eval(env); env.RedefineSymbol(name, definition); return definition; } public static Environment RegisterPrimitives() { Environment result = new Environment(); result.AddSymbol(new Symbol("lambda"), new ProcedureExpression(new PrimitiveProcedure(Lambda))); result.AddSymbol(new Symbol("quote"), new ProcedureExpression(new PrimitiveProcedure(Quote))); result.AddSymbol(new Symbol("define"), new ProcedureExpression(new PrimitiveProcedure(Define))); result.AddSymbol(new Symbol("car"), new ProcedureExpression(new PrimitiveProcedure(Car))); result.AddSymbol(new Symbol("cdr"), new ProcedureExpression(new PrimitiveProcedure(Cdr))); result.AddSymbol(new Symbol("cons"), new ProcedureExpression(new PrimitiveProcedure(Cons))); result.AddSymbol(new Symbol("quit"), new ProcedureExpression(new PrimitiveProcedure(Quit))); result.AddSymbol(new Symbol("cond"), new ProcedureExpression(new PrimitiveProcedure(Cond))); result.AddSymbol(new Symbol("+"), new ProcedureExpression(new PrimitiveProcedure(Plus))); result.AddSymbol(new Symbol("-"), new ProcedureExpression(new PrimitiveProcedure(Minus))); result.AddSymbol(new Symbol("*"), new ProcedureExpression(new PrimitiveProcedure(Times))); result.AddSymbol(new Symbol("/"), new ProcedureExpression(new PrimitiveProcedure(DividedBy))); result.AddSymbol(new Symbol("%"), new ProcedureExpression(new PrimitiveProcedure(Modulus))); result.AddSymbol(new Symbol("eq"), new ProcedureExpression(new PrimitiveProcedure(Eq))); result.AddSymbol(new Symbol("set!"), new ProcedureExpression(new PrimitiveProcedure(Set))); return result; } } }