From 40b87c1c0d8238d1f3ae5c912ca8d19ecafd64ba Mon Sep 17 00:00:00 2001 From: Max Kaufmann Date: Sat, 30 Jul 2011 13:41:03 -0700 Subject: [PATCH] first commit --- .gitignore | 3 + Lang/Expression.cs | 142 ++++++++++++++++++++++++++++++++++++++++++++ Lang/Instruction.cs | 97 ++++++++++++++++++++++++++++++ Lang/Interpreter.cs | 70 ++++++++++++++++++++++ Main.cs | 99 ++++++++++++++++++++++++++++++ README | 20 +++++++ RoboLogo.csproj | 47 +++++++++++++++ RoboLogo.sln | 20 +++++++ 8 files changed, 498 insertions(+) create mode 100644 .gitignore create mode 100644 Lang/Expression.cs create mode 100644 Lang/Instruction.cs create mode 100644 Lang/Interpreter.cs create mode 100644 Main.cs create mode 100644 README create mode 100644 RoboLogo.csproj create mode 100644 RoboLogo.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2847237 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +bin +*.pidb +*.userprefs \ No newline at end of file diff --git a/Lang/Expression.cs b/Lang/Expression.cs new file mode 100644 index 0000000..f425be1 --- /dev/null +++ b/Lang/Expression.cs @@ -0,0 +1,142 @@ +// ROBOLOGO +// Copyright (C) 2011 max.kaufmann@gmail.com +// +// 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 . + +using System; +namespace RoboLogo.Lang { + + /// + /// Interface for all instructions. Conditional expressions are treated as expr!=0 numeric expressions + /// + public abstract class Expression { + public abstract int Compute(Interpreter interp); + } + + /// + /// Implemented Binary Operations + /// + public enum BinaryOperation { Add, Subtract, Multiply, Divide, And, Or, Equals, GreaterThan, LessThan } + + /// + /// Implemented Unary Operations + /// + public enum UnaryOperation { Negate, Complement } + + /// + /// Null expression just returns 0 + /// + public class NullExpression : Expression { + override public int Compute(Interpreter interp) { + return 0; + } + } + + /// + /// A literal expression evaluates to a constant + /// + public class LiteralExpression : Expression { + int mValue; + + public LiteralExpression(int val) { + mValue = val; + } + + override public int Compute(Interpreter interp) { + return mValue; + } + } + + /// + /// A variable expression looks up a value in the current executing context + /// + public class VariableExpression : Expression { + string mName; + + public VariableExpression(string name) { + mName = name; + } + + override public int Compute(Interpreter interp) { + int result; + if (!interp.GetVariable(mName, out result)) { + Console.WriteLine("Undefined Variable Expression"); + } + return result; + } + } + + /// + /// Implementation of all binary operations + /// + public class BinaryOperationExpression : Expression { + BinaryOperation mOp; + Expression mLeft; + Expression mRight; + + public BinaryOperationExpression(BinaryOperation op, Expression left, Expression right) { + mOp = op; + mLeft = left; + mRight = right; + + } + + override public int Compute(Interpreter interp) { + switch(mOp) { + case BinaryOperation.Add: return mLeft.Compute(interp) + mRight.Compute(interp); + case BinaryOperation.Subtract: return mLeft.Compute(interp) - mRight.Compute(interp); + case BinaryOperation.Multiply: return mLeft.Compute(interp) * mRight.Compute(interp); + case BinaryOperation.Divide: + int r = mRight.Compute(interp); + if (r == 0) { + Console.WriteLine("Divide By Zero"); + return 0; + } else { + return mLeft.Compute(interp) / r; + } + case BinaryOperation.And: return (mLeft.Compute(interp) != 0) && (mRight.Compute(interp) != 0) ? 1 : 0; + case BinaryOperation.Or: return (mLeft.Compute(interp) != 0) || (mRight.Compute(interp) != 0) ? 1 : 0; + case BinaryOperation.Equals: return mLeft.Compute(interp) == mRight.Compute(interp) ? 1 : 0; + case BinaryOperation.GreaterThan: return mLeft.Compute(interp) > mRight.Compute(interp) ? 1 : 0; + case BinaryOperation.LessThan: return mLeft.Compute(interp) < mRight.Compute(interp) ? 1 : 0; + default: return 0; + } + } + } + + /// + /// Implementation of all unary operations + /// + public class UnaryOperationExpression : Expression { + UnaryOperation mOp; + Expression mExpr; + + public UnaryOperationExpression(UnaryOperation op, Expression exp) { + mOp = op; + mExpr = exp; + } + + override public int Compute(Interpreter interp) { + switch(mOp) { + case UnaryOperation.Negate: return -mExpr.Compute(interp); + case UnaryOperation.Complement: return mExpr.Compute(interp) != 0 ? 0 : 1; + default: return 0; + } + } + } + + + +} + diff --git a/Lang/Instruction.cs b/Lang/Instruction.cs new file mode 100644 index 0000000..30c81c0 --- /dev/null +++ b/Lang/Instruction.cs @@ -0,0 +1,97 @@ +// ROBOLOGO +// Copyright (C) 2011 max.kaufmann@gmail.com +// +// 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 . + +using System; + +namespace RoboLogo.Lang { + + /// + /// Interface for all instructions. + /// + public abstract class Instruction { + public abstract void Execute(Interpreter interp); + } + + /// + /// Implementation of a goto instruction + /// + public class GotoInstruction : Instruction { + int mIndex; + + public GotoInstruction(int index) { + mIndex = index; + } + + override public void Execute(Interpreter interp) { + interp.Goto(mIndex); + } + } + + /// + /// Implementation of a set-global variable instruction + /// + public class SetInstruction : Instruction { + string mName; + Expression mExpr; + + public SetInstruction(string name, Expression exp) { + mName = name; + mExpr = exp; + } + + override public void Execute(Interpreter interp) { + interp.SetVariable(mName, mExpr.Compute(interp)); + } + } + + /// + /// Implemention of a branch instruciton, used to implement conditionals and loops + /// + public class BranchInstruction : Instruction { + Expression mCondition; + int mTrueIndex; + int mFalseIndex; + + public BranchInstruction(Expression condition, int trueIndex, int falseIndex) { + mCondition = condition; + mTrueIndex = trueIndex; + mFalseIndex = falseIndex; + } + + override public void Execute(Interpreter interp) { + interp.Goto(mCondition.Compute(interp) == 0 ? mFalseIndex : mTrueIndex); + } + } + + /// + /// Implementation of a custom action instruction, for binding to user-space C# code + /// + public class ActionInstruction : Instruction { + Action mAction; + Expression mExpr; + + public ActionInstruction(Action action, Expression exp) { + mAction = action; + mExpr = exp; + } + + override public void Execute(Interpreter interp) { + mAction(mExpr.Compute(interp)); + } + } + +} + diff --git a/Lang/Interpreter.cs b/Lang/Interpreter.cs new file mode 100644 index 0000000..789b49b --- /dev/null +++ b/Lang/Interpreter.cs @@ -0,0 +1,70 @@ +// ROBOLOGO +// Copyright (C) 2011 max.kaufmann@gmail.com +// +// 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 . + +using System; +using System.Collections.Generic; + +namespace RoboLogo.Lang { + + /// + /// The interpreter is the facade-class for the virtual machine. It maintains an instruciton buffer, + /// an instruction pointer, and a dictionary of global variables. + /// + + public class Interpreter { + Instruction[] mInstructions; + int mCurrentInstruction; + int mNextInstruction; + Dictionary mEnvironment = new Dictionary(); + + public Interpreter (Instruction[] instructions) { + mInstructions = instructions; + mCurrentInstruction = 0; + } + + public bool ExecuteNextInstruction() { + if (mCurrentInstruction >= mInstructions.Length) { return false; } + mNextInstruction = mCurrentInstruction + 1; + mInstructions[mCurrentInstruction].Execute(this); + mCurrentInstruction = mNextInstruction; + return true; + } + + internal void Goto(int n) { + mNextInstruction = n; + } + + internal void SetVariable(string name, int val) { + if (!mEnvironment.ContainsKey(name)) { + mEnvironment.Add(name, val); + } else { + mEnvironment[name] = val; + } + } + + internal bool GetVariable(string name, out int val) { + if (!mEnvironment.ContainsKey(name)) { + val = 0; + return false; + } else { + val = mEnvironment[name]; + return true; + } + } + } + +} + diff --git a/Main.cs b/Main.cs new file mode 100644 index 0000000..524b11b --- /dev/null +++ b/Main.cs @@ -0,0 +1,99 @@ +// ROBOLOGO +// Copyright (C) 2011 max.kaufmann@gmail.com +// +// 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 . + +using RoboLogo.Lang; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace RoboLogo { + class MainClass { + + public static void Main (string[] args) { + // a quick interpreter test program (for-loop) + var interpreter = new Interpreter(StupidCompiler(@" + set i 0 + hello + set i i+1 + branch i<10 1 4 + ")); + while(interpreter.ExecuteNextInstruction()) {} + } + + //--------------------------------------------------------------------- + // Implementation of a "stupid" assembly compiler for testing the + // interpreter separate from the regular compiler + //--------------------------------------------------------------------- + + static Instruction[] StupidCompiler(string src) { + var buffer = new List(); + foreach(var line in src.Split('\n')) { + var trim = line.Trim(); + if (trim.Length > 0) { + var tokens = trim.Split(' '); + switch(tokens[0]) { + case "set": + buffer.Add( new SetInstruction(tokens[1], StupidExpressionParser(tokens[2])) ); + break; + case "hello": + buffer.Add( new ActionInstruction(arg=>Console.WriteLine("Hello, World"), new NullExpression()) ); + break; + case "branch": + buffer.Add( new BranchInstruction(StupidExpressionParser(tokens[1]), int.Parse(tokens[2]), int.Parse(tokens[3])) ); + break; + } + } + } + return buffer.ToArray(); + } + + static readonly Dictionary kOpLookup = InitOpTable(); + + static Dictionary InitOpTable() { + var result = new Dictionary(); + result.Add('+', BinaryOperation.Add); + result.Add('-', BinaryOperation.Subtract); + result.Add('*', BinaryOperation.Multiply); + result.Add('/', BinaryOperation.Divide); + result.Add('&', BinaryOperation.And); + result.Add('|', BinaryOperation.Or); + result.Add('=', BinaryOperation.Equals); + result.Add('>', BinaryOperation.GreaterThan); + result.Add('<', BinaryOperation.LessThan); + return result; + } + + static Expression StupidExpressionParser(string expr) { + int index = 0; + int literal; + while(!kOpLookup.ContainsKey(expr[index])) { + index++; + if (index == expr.Length) { + // not a binary operation + return int.TryParse(expr, out literal) ? (Expression) new LiteralExpression(literal) : (Expression) new VariableExpression(expr); + } + } + var left = expr.Substring(0, index); + var right = expr.Substring(index+1); + return new BinaryOperationExpression( + kOpLookup[expr[index]], + int.TryParse(left, out literal) ? (Expression) new LiteralExpression(literal) : (Expression) new VariableExpression(left), + int.TryParse(right, out literal) ? (Expression) new LiteralExpression(literal) : (Expression) new VariableExpression(right) + ); + } + } +} + diff --git a/README b/README new file mode 100644 index 0000000..47a8ce4 --- /dev/null +++ b/README @@ -0,0 +1,20 @@ +// ROBOLOGO +// Copyright (C) 2011 max.kaufmann@gmail.com +// +// 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 . + +An academic implementation of a LOGO-like scripting language for C#. I don't require optimization, so I'm designing for customizability and maintainability rather than performance. + +Comments/Questions welcome: max.kaufmann@gmail.com + diff --git a/RoboLogo.csproj b/RoboLogo.csproj new file mode 100644 index 0000000..74dddb6 --- /dev/null +++ b/RoboLogo.csproj @@ -0,0 +1,47 @@ + + + + Debug + x86 + 9.0.21022 + 2.0 + {2D2D601A-4CC0-4FFF-931D-CC8575CF4930} + Exe + RoboLogo + RoboLogo + v3.5 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + x86 + true + + + none + false + bin\Release + prompt + 4 + x86 + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RoboLogo.sln b/RoboLogo.sln new file mode 100644 index 0000000..7643e58 --- /dev/null +++ b/RoboLogo.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoboLogo", "RoboLogo.csproj", "{2D2D601A-4CC0-4FFF-931D-CC8575CF4930}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2D2D601A-4CC0-4FFF-931D-CC8575CF4930}.Debug|x86.ActiveCfg = Debug|x86 + {2D2D601A-4CC0-4FFF-931D-CC8575CF4930}.Debug|x86.Build.0 = Debug|x86 + {2D2D601A-4CC0-4FFF-931D-CC8575CF4930}.Release|x86.ActiveCfg = Release|x86 + {2D2D601A-4CC0-4FFF-931D-CC8575CF4930}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = RoboLogo.csproj + EndGlobalSection +EndGlobal