diff --git a/interpreter/LispPrimitives.cs b/interpreter/LispPrimitives.cs index 26d454c..c5dcc88 100644 --- a/interpreter/LispPrimitives.cs +++ b/interpreter/LispPrimitives.cs @@ -26,104 +26,6 @@ namespace KumiScript.Interpreter 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(); @@ -207,7 +109,7 @@ namespace KumiScript.Interpreter Expression antecedent = conditional.Car(); Expression consequent = conditional.Cdr().Car(); - if (!(antecedent.Eval(env) is NilExpression)) + if (antecedent.Eval(env) != NilExpression.GetInstance()) return consequent.Eval(env); } return NilExpression.GetInstance(); @@ -242,6 +144,93 @@ namespace KumiScript.Interpreter return definition; } + public static Expression For(ListExpression args, Environment env) + { + List argl = args.GetMembers(); + if (argl.Count != 4) + throw new InterpreterInvalidApplicationException(); + + Expression init = argl[0]; + Expression cond = argl[1]; + Expression step = argl[2]; + Expression body = argl[3]; + Expression result = NilExpression.GetInstance(); + Environment inner = new Environment(env); + + for (init.Eval(inner); cond.Eval(inner) != NilExpression.GetInstance(); step.Eval(inner)) + result = body.Eval(inner); + + return result; + } + + public static Expression Progn(ListExpression args, Environment env) + { + List argl = args.GetMembers(); + Expression result = NilExpression.GetInstance(); + + foreach (Expression expr in argl) + { + result = expr.Eval(env); + } + return result; + } + + public static Expression Prog1(ListExpression args, Environment env) + { + List argl = args.GetMembers(); + Expression result = argl.Count > 0 ? argl[0] : NilExpression.GetInstance(); + + foreach (Expression expr in argl) + expr.Eval(env); + + return result; + } + + public static Expression Not(ListExpression args, Environment env) + { + List argl = args.EvalMembers(env); + if (argl.Count != 1) + throw new InterpreterInvalidApplicationException(); + + return argl[0] != NilExpression.GetInstance() ? NilExpression.GetInstance() : TrueExpression.GetInstance(); + } + + public static Expression Nth(ListExpression args, Environment env) + { + List argl = args.EvalMembers(env); + if (argl.Count != 2) + throw new InterpreterInvalidApplicationException(); + + IntegerExpression? arg0 = argl[0] as IntegerExpression; + if (arg0 is null) + throw new InterpreterTypingException(); + + ListExpression? arg1 = argl[1] as ListExpression; + if (arg1 is null) + throw new InterpreterTypingException(); + + int n = arg0.GetValueAsInt(); + List arg1l = arg1.GetMembers(); + return n >= 0 && n < arg1l.Count ? arg1l[n] : NilExpression.GetInstance(); + } + + public static Expression While(ListExpression args, Environment env) + { + List argl = args.GetMembers(); + if (argl.Count != 2) + throw new InterpreterInvalidApplicationException(); + + Expression cond = argl[0]; + Expression body = argl[1]; + Expression result = NilExpression.GetInstance(); + Environment inner = new Environment(env); + + while (cond.Eval(inner) != NilExpression.GetInstance()) + result = body.Eval(inner); + + return result; + } + public static Environment RegisterPrimitives() { Environment result = new Environment(); @@ -253,14 +242,14 @@ namespace KumiScript.Interpreter 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))); - + result.AddSymbol(new Symbol("for"), new ProcedureExpression(new PrimitiveProcedure(For))); + result.AddSymbol(new Symbol("progn"), new ProcedureExpression(new PrimitiveProcedure(Progn))); + result.AddSymbol(new Symbol("not"), new ProcedureExpression(new PrimitiveProcedure(Not))); + result.AddSymbol(new Symbol("prog1"), new ProcedureExpression(new PrimitiveProcedure(Prog1))); + result.AddSymbol(new Symbol("nth"), new ProcedureExpression(new PrimitiveProcedure(Nth))); + result.AddSymbol(new Symbol("while"), new ProcedureExpression(new PrimitiveProcedure(While))); return result; } } diff --git a/interpreter/MathPrimitives.cs b/interpreter/MathPrimitives.cs new file mode 100644 index 0000000..dd047ff --- /dev/null +++ b/interpreter/MathPrimitives.cs @@ -0,0 +1,230 @@ +using System.Reflection.Metadata.Ecma335; + +namespace KumiScript.Interpreter +{ + public class MathPrimitives + { + 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 GreaterThan(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 arg0.GetValueAsFloat() > arg1.GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance(); + } + public static Expression LessThan(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 arg0.GetValueAsFloat() < arg1.GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance(); + } + + public static Expression LessThanEquals(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 arg0.GetValueAsFloat() <= arg1.GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance(); + } + + public static Expression GreaterThanEquals(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 arg0.GetValueAsFloat() >= arg1.GetValueAsFloat() ? TrueExpression.GetInstance() : NilExpression.GetInstance(); + } + + public static Expression PlusPlus(ListExpression args, Environment env) + { + List argl = args.GetMembers(); + if (argl.Count != 1) + throw new InterpreterInvalidApplicationException(); + + SymbolExpression? binding = argl[0] as SymbolExpression; + if (binding is null) + throw new InterpreterTypingException(); + + Symbol name = binding.GetSymbol(); + Expression val = env.Lookup(name); + NumberExpression? num = val as NumberExpression; + if (num is null) + throw new InterpreterTypingException(); + + NumberExpression nump = NumberFactory.NormalizeFloat(num.GetValueAsFloat() + 1); + env.RedefineSymbol(name, nump); + return nump; + } + + public static Expression MinusMinus(ListExpression args, Environment env) + { + List argl = args.GetMembers(); + if (argl.Count != 1) + throw new InterpreterInvalidApplicationException(); + + SymbolExpression? binding = argl[0] as SymbolExpression; + if (binding is null) + throw new InterpreterTypingException(); + + Symbol name = binding.GetSymbol(); + Expression val = env.Lookup(name); + NumberExpression? num = val as NumberExpression; + if (num is null) + throw new InterpreterTypingException(); + + NumberExpression nump = NumberFactory.NormalizeFloat(num.GetValueAsFloat() - 1); + env.RedefineSymbol(name, nump); + return nump; + } + + public static Environment RegisterPrimitives(Environment outer) + { + Environment result = new Environment(outer); + 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(">"), new ProcedureExpression(new PrimitiveProcedure(GreaterThan))); + result.AddSymbol(new Symbol("<"), new ProcedureExpression(new PrimitiveProcedure(LessThan))); + result.AddSymbol(new Symbol("<="), new ProcedureExpression(new PrimitiveProcedure(LessThanEquals))); + result.AddSymbol(new Symbol(">="), new ProcedureExpression(new PrimitiveProcedure(GreaterThanEquals))); + result.AddSymbol(new Symbol("++!"), new ProcedureExpression(new PrimitiveProcedure(PlusPlus))); + result.AddSymbol(new Symbol("--!"), new ProcedureExpression(new PrimitiveProcedure(MinusMinus))); + return result; + } + } +} \ No newline at end of file diff --git a/interpreter/ReadEvalPrintLoop.cs b/interpreter/ReadEvalPrintLoop.cs index c5d8f4d..8433d53 100644 --- a/interpreter/ReadEvalPrintLoop.cs +++ b/interpreter/ReadEvalPrintLoop.cs @@ -17,7 +17,7 @@ namespace KumiScript.Interpreter Lexer lexer = new Lexer(_stdin); Parser parser = new Parser(lexer); StreamWriter streamWriter = new StreamWriter(_stdout); - Environment top = LispPrimitives.RegisterPrimitives(); + Environment top = MathPrimitives.RegisterPrimitives(LispPrimitives.RegisterPrimitives()); while (true) //this thing is ugly but it's just a little test {