samples: New: ecas, curvyweb, gltriangle
authorJerome St-Louis <jerome@ecere.com>
Tue, 27 Sep 2011 20:48:52 +0000 (16:48 -0400)
committerJerome St-Louis <jerome@ecere.com>
Tue, 27 Sep 2011 20:48:52 +0000 (16:48 -0400)
samples/3D/glTriangle/glTriangle.ec [new file with mode: 0644]
samples/3D/glTriangle/glTriangle.epj [new file with mode: 0644]
samples/eC/ecas/ecas.ec [new file with mode: 0644]
samples/eC/ecas/ecas.epj [new file with mode: 0644]
samples/eC/ecas/expression.ec [new file with mode: 0644]
samples/eC/ecas/misc.ec [new file with mode: 0644]
samples/guiAndGfx/curvyweb/curvyweb.ec [new file with mode: 0644]
samples/guiAndGfx/curvyweb/curvyweb.epj [new file with mode: 0644]

diff --git a/samples/3D/glTriangle/glTriangle.ec b/samples/3D/glTriangle/glTriangle.ec
new file mode 100644 (file)
index 0000000..045e4f0
--- /dev/null
@@ -0,0 +1,32 @@
+#include <GL/gl.h>
+import "ecere"
+
+class GLTriangle : Window
+{
+   text = "Triangle";
+   displayDriver = "OpenGL";
+   background = activeBorder;
+   nativeDecorations = true;
+   borderStyle = sizable;
+   hasMaximize = true, hasMinimize = true, hasClose = true;
+   size = { 640, 480 };
+
+   void OnRedraw(Surface surface)
+   {
+      glMatrixMode(GL_PROJECTION);
+      glLoadIdentity();
+      glOrtho(-30, 30, -30, 30, -30, 30);
+      glMatrixMode(GL_MODELVIEW);
+      glLoadIdentity();
+      glTranslatef(-15, -15, 0);
+      glShadeModel(GL_SMOOTH);
+
+      glBegin(GL_TRIANGLES);
+      glColor3f(1, 0, 0);  glVertex2f(0, 0);
+      glColor3f(0, 1, 0);  glVertex2f(30, 0);
+      glColor3f(0, 0, 1);  glVertex2f(0, 30);
+      glEnd();
+   }
+}
+
+GLTriangle window {};
diff --git a/samples/3D/glTriangle/glTriangle.epj b/samples/3D/glTriangle/glTriangle.epj
new file mode 100644 (file)
index 0000000..3395c26
--- /dev/null
@@ -0,0 +1,56 @@
+{
+   "Version" : 0.2,
+   "ModuleName" : "glTriangle",
+   "Options" : {
+      "Warnings" : "All",
+      "TargetType" : "Executable",
+      "TargetFileName" : "glTriangle",
+      "Libraries" : [
+         "ecere"
+      ]
+   },
+   "Platforms" : [
+      {
+         "Name" : "win32",
+         "Options" : {
+            "Libraries" : [
+               "opengl32"
+            ]
+         }
+      },
+      {
+         "Name" : "linux",
+         "Options" : {
+            "Libraries" : [
+               "GL"
+            ]
+         }
+      }
+   ],
+   "Configurations" : [
+      {
+         "Name" : "Debug",
+         "Options" : {
+            "Debug" : true,
+            "Optimization" : "None",
+            "PreprocessorDefinitions" : [
+               "_DEBUG"
+            ]
+         }
+      },
+      {
+         "Name" : "Release",
+         "Options" : {
+            "Debug" : false,
+            "Optimization" : "Speed"
+         }
+      }
+   ],
+   "Files" : [
+      "glTriangle.ec"
+   ],
+   "ResourcesPath" : "",
+   "Resources" : [
+
+   ]
+}
\ No newline at end of file
diff --git a/samples/eC/ecas/ecas.ec b/samples/eC/ecas/ecas.ec
new file mode 100644 (file)
index 0000000..1a62ea5
--- /dev/null
@@ -0,0 +1,98 @@
+// ecas -- By Joey Adams
+import "ecere"
+import "expression"
+
+class Form1 : Window
+{
+   text = "Simple Parser";
+   background = activeBorder;
+   borderStyle = sizable;
+   hasMaximize = true;
+   hasMinimize = true;
+   hasClose = true;
+   size = { 576, 432 };
+   anchor = { horz = -3, vert = -4 };
+
+   bool OnCreate(void)
+   {
+      
+      return true;
+   }
+
+   bool OnKeyHit(Key key, unichar ch)
+   {
+      if (ch=='\r')
+         DoParse(true);
+      
+      return true;
+   }
+
+   void DoParse(bool simplify) {
+      EditBoxStream s;
+      CASDictionary dict {};
+      Expression expr {};
+      Expression exprCopy;
+      outputTree.Clear();
+      s = {editBox=outputTree};
+      if (expr.FromString(inputString.contents, dict)) {
+         statusLabel.SetText("Parse successful");
+         if (simplify)
+            expr.Simplify(dict);
+      }
+      else
+         statusLabel.SetText("Invalid expression");
+      expr.DebugPrint(s, dict, 0, null);
+      s.Printf("\nCopy:\n");
+      exprCopy = expr.Copy(null);
+      delete expr;
+      exprCopy.DebugPrint(s, dict, 0, null);
+      delete s;
+      delete dict;
+      delete exprCopy;
+   }
+
+   Button parseButton
+   {
+      this, text = "Parse", size = { 75, 30 }, position = { 10, 40 };
+
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         DoParse(false);
+         return true;
+      }
+   };
+   Button simplifyButton
+   {
+      this, text = "Simplify", size = { 75, 30 }, position = { 90, 40 };
+
+      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
+      {
+         DoParse(true);
+         return true;
+      }
+   };
+
+   bool TypeButton(Button button, int x, int y, Modifiers mods) {
+      inputString.Activate();
+      inputString.Deselect();
+      inputString.AddS(button.text);
+      inputString.Update(null);
+   }
+   Label statusLabel { this, size = { 274, 21 }, position = { 250, 45 } };
+   Button ebutton { this, text = constant_string[e], NotifyClicked = TypeButton, size = { 20, 20 }, position = { 170, 45 } };
+   Button ibutton { this, text = constant_string[i], NotifyClicked = TypeButton, size = { 20, 20 }, position = { 195, 45 } };
+   Button pibutton { this, text = constant_string[pi], NotifyClicked = TypeButton, size = { 20, 20 }, position = { 220, 45 } };
+   EditBox outputTree { this, font = { "Courier New", 10 }, size = { 286, 219 }, position = { 10, 80 }, hasHorzScroll = true, true, readOnly = true, true };
+   EditBox inputString { this, text = "inputString", size = { 350, 19 }, position = { 10, 10 } };
+
+   void OnResize(int width, int height)
+   {
+      inputString.size.w = clientSize.w-20;
+      //parseButton.position.x = (clientSize.w-parseButton.size.w)/2;
+      statusLabel.size.w = clientSize.w-statusLabel.position.x-10;
+      outputTree.size.w = clientSize.w-20;
+      outputTree.size.h = clientSize.h-outputTree.position.y-10;
+   }
+}
+
+Form1 form1 {};
diff --git a/samples/eC/ecas/ecas.epj b/samples/eC/ecas/ecas.epj
new file mode 100644 (file)
index 0000000..da2137d
--- /dev/null
@@ -0,0 +1,46 @@
+{
+   "Version" : 0.2,
+   "ModuleName" : "ecas",
+   "Options" : {
+      "Warnings" : "All",
+      "TargetType" : "Executable",
+      "Libraries" : [
+         "ecere"
+      ]
+   },
+   "Configurations" : [
+      {
+         "Name" : "Debug",
+         "Options" : {
+            "Debug" : true,
+            "Optimization" : "None",
+            "PreprocessorDefinitions" : [
+               "_DEBUG"
+            ],
+            "TargetFileName" : "ecas",
+            "TargetDirectory" : "debug",
+            "ObjectsDirectory" : "debug",
+            "Console" : true
+         }
+      },
+      {
+         "Name" : "Release",
+         "Options" : {
+            "Debug" : false,
+            "Optimization" : "Speed",
+            "TargetFileName" : "ecas",
+            "TargetDirectory" : "release",
+            "ObjectsDirectory" : "release"
+         }
+      }
+   ],
+   "Files" : [
+      "misc.ec",
+      "expression.ec",
+      "ecas.ec"
+   ],
+   "ResourcesPath" : "",
+   "Resources" : [
+
+   ]
+}
\ No newline at end of file
diff --git a/samples/eC/ecas/expression.ec b/samples/eC/ecas/expression.ec
new file mode 100644 (file)
index 0000000..6dd69e4
--- /dev/null
@@ -0,0 +1,890 @@
+public import "ecere"
+import "misc"
+
+#include <stdio.h>
+
+public:
+
+enum CASCharFlag {letter, end, white, number, op};
+
+class CASCharFlags : CharFlags {
+   CASCharFlags() {
+      flags[0] = (byte)CASCharFlag::end;
+      Set("\t\n\v\f\r ", CASCharFlag::white);
+      Set("0123456789.", CASCharFlag::number);
+      Set("!%&()*+,-/:;<=>?@[\\]^{|}~", CASCharFlag::op);
+   }
+};
+
+CASCharFlags cascharflags {};
+
+enum ExpressionType {null=0, i, f, c, v, openfunction, function, openparenthesis, parenthesis, prefix, postfix, binary};
+   //i means integer
+   //f means floating point
+   //c means constant
+   //v means variable
+
+enum ExpressionClass {nullary=0, prefix, postfix, binary, vallary, nary};
+//I define a vallary operator as one that binds a single expression on both sides (e.g. (), [], {}, "")
+//An nary operator is one that binds an array of expressions on both sides (e.g. sum(3,4,5))
+
+ExpressionClass expression_class[ExpressionType] = {
+   nullary, nullary, nullary, nullary, nullary, prefix, nary, prefix, vallary, prefix, postfix, binary
+};
+//function and parenthesis are weird classes.  They are considered nullary, but they do contain data:
+//  function's arguments are in the Array<Expression> arg
+//  parenthesis's argument is in b, just like the prefix it was before
+
+enum PrefixOperator {root=0, positive, negative};
+int prefix_op_order[PrefixOperator] = {
+   0, 4,4
+};
+const char *prefix_op_string[PrefixOperator] = {
+   "root", "+", "-"
+};
+
+enum PostfixOperator {factorial=0};
+int postfix_op_order[PostfixOperator] = {
+   6
+};
+const char *postfix_op_string[PostfixOperator] = {
+   "!"
+};
+
+//A lot of CAS operations may weed out operators like subtract, but it is included to make it possible to maniuplate the expression for readability, etc.
+enum BinaryOperator {add, subtract, multiply, divide, power, equals, comma};
+int binary_op_order[BinaryOperator] = {
+   3,3, 4,4, 5, 2, 1
+};
+const char *binary_op_string[BinaryOperator] = {
+   "+", "-", "*", "/", "^", "=", ","
+};
+
+enum ParenthesisType {parenthesis, brackets, braces};
+const char *parenthesis_type_string[ParenthesisType] = {
+   "()", "[]", "{}"
+};
+
+enum CASFunction {sum, product, polynomial, exp, ln, sin, cos, tan, arcsin, arccos, arctan};
+
+const char *function_string[CASFunction] = {
+   "sum", "product", "polynomial", "exp", "ln", "sin", "cos", "tan", "arcsin", "arccos", "arctan"
+};
+
+class CASFunctionDefinition {
+   virtual bool Expression::Sanitize(void) {
+      return true;
+   }
+   virtual void Expression::Simplify(void);
+};
+
+bool Expression::SanitizeSingleArgument(void) {
+   if (arg.size != 1)
+      return false;
+   return true;
+}
+
+class FunctionList : Dictionary {
+   Array<CASFunctionDefinition> f {};
+   FunctionList() {
+      CASFunction i;                   
+      for (i=0; i<CASFunction::enumSize; i++)
+         array.Add(StrDup(function_string[i]));
+      f.size = CASFunction::enumSize;
+      f[CASFunction::sum] = {
+         
+      };
+      f[CASFunction::product] = {
+         
+      };
+      f[CASFunction::polynomial] = {
+         
+      };
+      f[CASFunction::exp] = {
+         bool Expression::Sanitize()
+         {
+            Expression right;
+            if (arg.size != 1)
+               return false;
+            right = arg[0];
+            delete arg;
+            type = binary;
+            binary.op = power;
+            a = {type=c, mom=this, c=e};
+            b = right;
+            return true;
+         }
+      };
+      f[CASFunction::ln] = {
+         Sanitize = SanitizeSingleArgument;
+         
+      };
+      f[CASFunction::sin] = {
+         Sanitize = SanitizeSingleArgument;
+         
+      };
+      f[CASFunction::cos] = {
+         Sanitize = SanitizeSingleArgument;
+         
+      };
+      f[CASFunction::tan] = {
+         Sanitize = SanitizeSingleArgument;
+         
+      };
+      f[CASFunction::arcsin] = {
+         Sanitize = SanitizeSingleArgument;
+         
+      };
+      f[CASFunction::arccos] = {
+         Sanitize = SanitizeSingleArgument;
+         
+      };
+      f[CASFunction::arctan] = {
+         Sanitize = SanitizeSingleArgument;
+         
+      };
+   }
+   //these functions check to make sure the input is a function of correct range before executing
+   bool Sanitize(Expression expr) {
+      bool ret = false;
+      if (expr.type==function && expr.function.func<f.size)
+         ret = f[expr.function.func].Sanitize(expr);
+      return ret;
+   }
+   void Simplify(Expression expr) {
+      if (expr.type==function && expr.function.func<f.size)
+         f[expr.function.func].Simplify(expr);
+   }
+};
+
+class CASDictionary : struct {
+   Dictionary v {};
+   FunctionList f {};
+};
+
+enum CASConstant {i, e, pi};
+
+const char *constant_string[CASConstant] = {
+   "𝑖", "ℯ", "π"
+};
+
+class ExprChildWalker {
+public:
+   Expression expr;
+   Expression user0, user1, user2;
+   
+   void Go(void) {
+      switch (expr.ExprClass()) {
+         case prefix:
+         case vallary:
+            On(&expr.b);
+            break;
+         case postfix:
+            On(&expr.a);
+            break;
+         case binary:
+            if (On(&expr.a))
+               On(&expr.b);
+            break;
+         case nary:
+            for (iter:expr.arg) {
+               if (!On(&iter))
+                  break;
+            }
+            break;
+      }
+   }
+   //return false to end iteration
+   virtual bool On(Expression *e);
+};
+
+class Expression : struct {
+public:
+   ExpressionType type;
+   Expression mom; //easier to type than "parent" :)
+
+   //tags
+   bool constant:1, approx:1;
+   void ClearTags(void) {
+      constant = false;
+      approx = false;
+   }
+
+   union {
+      long long i;
+      long double f;
+      CASConstant c;
+      uint v;
+      struct {
+         CASFunction func;
+      } function;
+      struct {
+         ParenthesisType type;
+      } parenthesis; //also used for openparenthesis
+      struct {
+         PrefixOperator op;
+      } prefix;
+      struct {
+         PostfixOperator op;
+      } postfix;
+      struct {
+         BinaryOperator op;
+      } binary;
+   };
+   union {
+      struct {
+         Expression a; //binary left child or postfix child
+         Expression b; //binary right child, prefix child, or vallary child
+      };
+      Array<Expression> arg; //only for nary operators (e.g. functions)
+   };
+
+   void DebugPrint(File out, CASDictionary dict, uint spacelevel, Expression correctMom) {
+      uint d = spacelevel;
+      char tagstr[16];
+      while (d--)
+         out.Putc(' ');
+      if (this==null) {
+         out.Printf("null pointer\n");
+         return;
+      }
+      if (mom != correctMom)
+         out.Printf("Incorrectly parented to %p instead of %p ", mom, correctMom);
+      memset(tagstr, '-', 2);
+      tagstr[2] = 0;
+      if (constant)
+         tagstr[0] = 'c';
+      if (approx)
+         tagstr[1] = 'a';
+      out.Printf("%s  ", tagstr);
+      switch (type) {
+         case null:
+            out.Printf("nothing\n");
+            break;
+         case i:
+            //out.PrintLn(i);
+            out.Printf("%lld\n", i);
+            break;
+         case f:
+            //out.PrintLn(f);
+            out.Printf("%.15Lg\n", (long double)f);
+            break;
+         case c:
+            out.Printf("%s (constant)\n", c<CASConstant::enumSize ? constant_string[c] : "Invalid constant");
+            break;
+         case v:
+            out.Printf("%s\n", dict.v.name(v));
+            break;
+         case openfunction:
+            out.Printf("%s(\n", dict.f.name(function.func));
+            b.DebugPrint(out, dict, spacelevel+2, this);
+            break;
+         case function:
+            out.Printf("%s()\n", dict.f.name(function.func));
+            for (iter:arg)
+               iter.DebugPrint(out, dict,spacelevel+2, this);
+            break;
+         case openparenthesis:
+            out.Printf("%c\n", OpString()[0]);
+            b.DebugPrint(out, dict, spacelevel+2, this);
+            break;
+         case parenthesis:
+            out.Printf("%s\n", OpString());
+            b.DebugPrint(out, dict, spacelevel+2, this);
+            break;
+         case prefix:
+            out.Printf("%s\n", OpString());
+            b.DebugPrint(out, dict, spacelevel+2, this);
+            break;
+         case postfix:
+            out.Printf("%s\n", OpString());
+            a.DebugPrint(out, dict, spacelevel+2, this);
+            break;
+         case binary:
+            out.Printf("%s\n", OpString());
+            a.DebugPrint(out, dict, spacelevel+2, this);
+            b.DebugPrint(out, dict, spacelevel+2, this);
+            break;
+      }
+   }
+   
+   void Free(void) {
+      ExprChildWalker w {this;
+         bool On(Expression *e) {
+            e->Clear();
+            if (*e)
+               e->mom = null;
+            delete *e;
+            return true;
+         }
+      };
+      w.Go();
+      if (ExprClass()==nary)
+         delete arg;
+      delete w;
+   }
+   void Clear(void) { //keeps parent the same
+      if (this) {
+         Free();
+         type = null;
+      }
+   }
+   //sort of like delete this, but does not affect anything else but this block
+   //Note:  Make sure that arg array is taken care of if this is an nary
+   void Delete(void) {
+      if (this) {
+         type = null;
+         mom = null;
+         delete this;
+      }
+   }
+   ~Expression() {
+      if (this) {
+         Elope(null);
+         Free();
+      }
+   }
+   //Copy() makes a copy of this Expression that makes no references to this expression
+   Expression Copy(Expression newMom) {
+      Expression ret {mom=newMom};
+      if (!this)
+         return ret;
+      memcpy(ret, this, sizeof(class Expression));
+      ret.mom = newMom;
+      switch (ExprClass()) {
+         case prefix:
+         case vallary:
+            ret.b = b.Copy(ret);
+            break;
+         case postfix:
+            ret.a = a.Copy(ret);
+            break;
+         case binary:
+            ret.a = a.Copy(ret);
+            ret.b = b.Copy(ret);
+            break;
+         case nary: {
+            uint i,e;
+            e = arg.size;
+            ret.arg = {size=e};
+            for (i=0; i<e; i++)
+               ret.arg[i] = arg[i].Copy(ret);
+         }
+            break;
+      }
+      return ret;
+   }
+   
+   //collect terms in this expression separated by the binary operator op
+   //e.g. separate x^2+3x+1 by plus into [x^2, 3x, 1]
+   //Will separate (x^2+3x+1)/2 by plus into [(x^2+3x+1)/2]
+   //If newMom is not null, all binary operator nodes involved in the array will be freed, and each node will be parented to newMom
+   void CollectTerms(BinaryOperator op, Array<Expression> out, Expression newMom) {
+      if (!this)
+         return;
+      if (type==binary && binary.op==op) {
+         a.CollectTerms(op, out, newMom);
+         b.CollectTerms(op, out, newMom);
+         if (newMom) {
+            a=null;
+            b=null;
+            delete this;
+         }
+      } else {
+         if (newMom)
+            mom = newMom;
+         out.Add(this);
+      }
+   }
+   //only reparents the top level
+   void ReparentChildren(void) {
+      ExprChildWalker w {this;
+         bool On(Expression *e)
+         {
+            e->mom = expr;
+            return true;
+         }
+      };
+      w.Go();
+      delete w;
+   }
+   //relinks the parent to this child
+   //Old is the former pointer to the child
+   void Rechild(Expression old) {
+      SwapChild(mom, old, this);
+   }
+   //disconnects this from parent without deleting it, and sets in its place newChild
+   void Elope(Expression newChild) {
+      SwapChild(mom, this, newChild);
+      if (newChild)
+         newChild.mom = mom;
+   }
+   //disconnects this expression from its parent and relinks its children to the parent
+   //Currently, this only works with unary (prefix/postfix/vallary) operators
+   void Unlink(void) {
+      if (!this) {
+         fprintf(stderr, "BUG:  Attempted to unlink null node; ignoring.\n");
+         return;
+      }
+      if (!mom) {
+         fprintf(stderr, "BUG:  Attempted to unlink root node; ignoring.\n");
+         return;
+      }
+      switch (ExprClass()) {
+         case prefix:
+         case vallary:
+            Elope(b);
+            Delete();
+            break;
+         case postfix:
+            Elope(a);
+            Delete();
+            break;
+         default:
+            fprintf(stderr, "BUG:  Attempted to call Unlink on non-unary node.\n");
+      }
+   }
+   
+   //Note that FromString likes to toy with the string, but anything done to the string will be undone.
+   bool FromString(char *str, CASDictionary dictionary) {
+      ExpressionFromString(this, str, dictionary);
+      return Sanitize(this, dictionary);
+   }
+   char *ToString(void) {
+      
+   }
+   const char *OpString(void) {
+      if (type==prefix && prefix.op<PrefixOperator::enumSize)
+         return prefix_op_string[prefix.op];
+      else if (type==postfix && postfix.op<PostfixOperator::enumSize)
+         return postfix_op_string[postfix.op];
+      else if (type==binary && binary.op<BinaryOperator::enumSize)
+         return binary_op_string[binary.op];
+      else if ((type==openparenthesis || type==parenthesis) && parenthesis.type<ParenthesisType::enumSize)
+         return parenthesis_type_string[parenthesis.type];
+      else
+         return "??";
+   }
+   int OpOrder(void) {
+      if (type==binary && binary.op<BinaryOperator::enumSize)
+         return binary_op_order[binary.op];
+      else if (type==prefix && prefix.op<PrefixOperator::enumSize)
+         return prefix_op_order[prefix.op];
+      else if (type==postfix && postfix.op<PostfixOperator::enumSize)
+         return postfix_op_order[postfix.op];
+      else if (type==openfunction) {
+         if (b && b.type==parenthesis && b.parenthesis.type==parenthesis)
+            return 1000; //arbitrary number larger than any other order
+         else
+            return binary_op_order[power];
+      }
+      else if (type==openparenthesis)
+         return 0; //yes, this should be the lowest, not highest, operator
+      else
+         return -1; //error
+   }
+   ExpressionClass ExprClass(void) {
+      if (type<ExpressionType::enumSize)
+         return expression_class[type];
+      else
+         return nullary;
+   }
+   void Simplify(CASDictionary dict) {
+      
+   }
+};
+
+void SwapChild(Expression mom, Expression oldChild, Expression newChild) {
+   ExprChildWalker w;
+   if (!mom)
+      return;
+   w = {mom, user0=oldChild, user1=newChild;
+      bool On(Expression *e) {
+         if (*e==user0) {
+            *e = user1;
+            return false;
+         }
+         return true;
+      }
+   };
+   w.Go();
+   delete w;
+}
+
+class ExpressionTree : struct {
+public:
+   CASDictionary dict;
+   Expression root {type=prefix, prefix.op=root};
+   Expression p; //pointer to most recently added token
+   File err {output=stderr};
+   bool closeerrfiles;
+
+   p = root;
+
+   ~ExpressionTree() {
+      if (!closeerrfiles) {
+         err.input = null;
+         err.output = null;
+      }
+   }
+
+   // e must be the string terminator, and the string must be zero-terminated as well
+   void PushInteger(const char *s, const char *e) {
+      ReadULLError rerror;
+      Expression var {type=i};
+      unsigned long long n = ReadULL_Valid(s, e, NB_Dec, &rerror);
+      var.i = (long long)n;
+      if (rerror==overflow || n>=(((unsigned long long)-1)>>1)+1)
+         err.Printf("Number %s too big; using %lld\n", s, var.i);
+      push(var, false);
+   }
+   void PushFloat(const char *s) {
+      Expression var {type=f};
+      long double n = (long double)atof(s); //TODO:  Use something that won't truncate the result
+      var.f = n;
+      push(var, false);
+   }
+   void PushConstant(CASConstant con) {
+      Expression var {type=c, c=con};
+      push(var, false);
+   }
+   void PushVariable(const char *s) {
+      Expression var {type=v, v=dict.v.lookup(s, true)};
+      push(var, false);
+   }
+   void PushFunction(CASFunction f) {
+      Expression var {type=openfunction, function.func=f};
+      push(var, false);
+   }
+   void PushOperator(const char *s) {
+      Expression op {type=binary};
+      Expression w;
+      ExpressionClass pc;
+      int order;
+      if (s[1]==0) { //single-character operator
+         switch (*s) {
+            case '+':
+            case '-':
+               pc = p.ExprClass();
+               if (pc==prefix || pc==binary) {
+                  op.type = prefix;
+                  op.prefix.op = *s=='+' ? positive : negative;
+               } else {
+                  op.binary.op = *s=='+' ? add : subtract;
+               }
+               break;
+            case '*':
+               op.binary.op = multiply;
+               break;
+            case '/':
+               op.binary.op = divide;
+               break;
+            case '^':
+               op.binary.op = power;
+               break;
+            case '=':
+               op.binary.op = equals;
+               break;
+            case ',':
+               op.binary.op = comma;
+               break;
+            case '(': //we will push a "prefix" openparenthesis like any other prefix op
+               op.type = openparenthesis;
+               op.parenthesis.type = parenthesis;
+               break;
+            case '[':
+               op.type = openparenthesis;
+               op.parenthesis.type = brackets;
+               break;
+            case '{':
+               op.type = openparenthesis;
+               op.parenthesis.type = braces;
+               break;
+            case ')': //we will close the soonest openparenthesis and continue from there
+            case ']':
+            case '}': {
+               ParenthesisType type = (*s==')' ? parenthesis : *s==']' ? brackets : braces);
+               delete op;
+               w = p;
+               for (;;) {
+                  if (w.type==openfunction)
+                     closeFunction(w);
+                  else if (w.type==openparenthesis) {
+                     if (w.parenthesis.type==type) {
+                        p = w;
+                        p.type = parenthesis;
+                        return;
+                     } else
+                        err.Printf("End %c appeared after start %c; disregarding.\n", *s, w.OpString()[1]);
+                  } else if (!w.mom || (w.type==prefix && w.prefix.op==root)) {
+                     err.Printf("End parenthesis missing beginning parenthesis; ignoring.\n");
+                     return;
+                  }
+                  w = w.mom;
+               }
+               break;
+            }
+            case '!':
+               op.type = postfix;
+               op.postfix.op = factorial;
+               break;
+            default:
+               op.type = null;
+         }
+      } else
+         op.type = null;
+      if (op.type==null) {
+         err.Printf("Operator '%s' unrecognized; ignoring.\n", s);
+         delete op;
+         return;
+      }
+      push(op, false);
+   }
+private:
+   void push(Expression expr, bool implicit) {
+      ExpressionClass c = expr.ExprClass();
+      ExpressionClass pc = p.ExprClass();
+      if (c==nullary || c==prefix) {
+         if (pc==nullary || pc==postfix || pc==vallary || pc==nary)
+            push({type=binary, binary.op=multiply}, true); //implicit multiply (e.g. 7a -> 7*a)
+         p.b = expr;
+         expr.mom = p;
+         p = expr;
+      } else {
+         int order;
+         Expression w;
+         if (pc==prefix || pc==binary) {
+            err.Printf("%s operator %s following operator %s; ignoring.\n", c==postfix ? "Postfix" : "Binary", expr.OpString(), p.OpString());
+            delete expr;
+            return;
+         }
+         order = expr.OpOrder();
+         if (order<0) {
+            err.Printf("BUG:  %s operator has invalid order; ignoring.\n", c==postfix ? "Postfix" : "Binary");
+            delete expr;
+            return;
+         }
+         //An AB operator is defined as a postfix or binary operator
+         //For any binary operators that associate right-to-left, increment order so they can overtake sooner
+         //Believe it or not, equals associates right-to-left (at least in C it does)
+         if (expr.type==binary && (expr.binary.op==power || expr.binary.op==equals))
+            order++;
+         //We will walk up the tree with this AB operator until we find a w such that w.mom.order < op.order
+         //If we reach the root node, that's a bug because the root node should have an order of 0
+         w = p;
+         for (;;) {
+            int worder;
+            if (!w.mom) {
+               err.Printf("BUG:  Expression tree ended too soon while handling binary operator; ignoring.\n");
+               delete expr;
+               return;
+            }
+            worder = w.mom.OpOrder();
+            if (worder<0) {
+               err.Printf("BUG:  Bad parent encountered while handling postfix/binary operator.\n");
+               delete expr;
+               return;
+            }
+            if (implicit && expr.type==binary && expr.binary.op==multiply && w.mom.type==openfunction) {
+               //this makes it so sin 3x will be sin(3x), while sin 3*x will be (sin 3)*x
+               if (worder == binary_op_order[power])
+                  break;
+            } else if (order > worder)
+               break;
+            w = w.mom;
+            if (w.type==openfunction)
+               closeFunction(w);
+         }
+         //We will take w, make it op's left child, and put op where w was.
+         expr.a = w;
+         expr.mom = w.mom;
+         w.mom = expr;
+         expr.mom.b = expr;
+         p = expr;
+      }
+   }
+   //closes f (should only be called if f is an openfunction)
+   void closeFunction(Expression f) {
+      Expression child = f.b;
+      if (child && child.type==parenthesis && child.parenthesis.type==parenthesis) {
+         //remove the first set of parenthesis
+         //if more sets of parenthesis surround this, the user really meant to have them there
+         Expression tmp = child.b;
+         child.b = null;
+         delete f.b;
+         child = f.b = tmp;
+      }
+      //Turn this from an openfunction into a function
+      f.b = null;
+      f.Free();
+      f.type = function;
+      f.arg = {};
+         //note that f.function.func persists because Free() did not clear that
+      //Collect all comma-delimited arguments into the arg array
+      child.CollectTerms(comma, f.arg, f);
+   }
+   void finalize(void) {
+      Expression w = p;
+      while (w) {
+         if (w.type==openfunction)
+            closeFunction(w);
+         w = w.mom;
+      }
+   }
+};
+
+void ExpressionFromString(Expression expr, char *str, CASDictionary dictionary) {
+   ExpressionTree tree {dictionary};
+   char *s, *p, *e; //token start and end
+   CASCharFlag flag;
+   CASCharFlags cf = cascharflags;
+   char borrow;
+
+   expr.Free();
+   
+   s = str;
+   e = s;
+   while (*s) {
+      for (;;) //for loop is used so multiple character types that merge into one token can be implemented in the future
+      { //grab the next token
+         byte f = cf.flags[(byte)*e++];
+         while (cf.flags[(byte)*e]==f) e++;
+         flag = (CASCharFlag)f;
+         break;
+      }
+      p = s;
+      switch (flag) {
+         case letter: {
+            uint f;
+            uint i;
+            int charlen;
+            char borrow2;
+            borrow = *e;
+            *e = 0;
+            f = dictionary.f.lookup(s, false);
+            if (f != (uint)-1)
+               tree.PushFunction((CASFunction)f);
+            else do {
+               unsigned char c;
+               uint con = (uint)-1;
+               UTF8GetChar(p, &charlen);
+               p += charlen;
+               borrow2 = *p;
+               *p = 0;
+               for (i=0; i<CASConstant::enumSize; i++) {
+                  if (!strcmp(s, constant_string[(CASConstant)i])) {
+                     con = i;
+                     break;
+                  }
+               }
+               if (con != (uint)-1)
+                  tree.PushConstant((CASConstant)con);
+               else
+                  tree.PushVariable(s);
+               *p = borrow2;
+               s = p;
+            } while (s<e);
+            *e = borrow;
+         }
+            break;
+         case number:
+            borrow = *e;
+            *e = 0;
+            if (strchr(s, '.'))
+               tree.PushFloat(s);
+            else
+               tree.PushInteger(s, e);
+            *e = borrow;
+            break;
+         case op:
+            //This only lexes one-character operators
+            do {
+               borrow = p[1];
+               p[1] = 0;
+               tree.PushOperator(p);
+               p[1] = borrow;
+               p++;
+            } while (p<e);
+            break;
+      }
+      s = e;
+   }
+   tree.finalize();
+   memcpy(expr, tree.root, sizeof(class Expression));
+   if (expr.b)
+      expr.b.mom = expr;
+   tree.root = null;
+   delete tree;
+}
+
+//This function walks the tree to make sure it is valid:
+//    no null expressions
+//    no open parenthesis/functions
+//    correct number of arguments for core functions (TODO)
+//While it's at it, it tags every expression in the tree
+//It also converts exp(foo) to e^foo (TODO)
+//returns true if the expression is valid and not empty
+bool Expression::Sanitize(CASDictionary dict) {
+   if (!this || this.type==null)
+      return false;
+   ClearTags();
+   #define S(x) {if (!Sanitize(x, dict)) return false;}
+   switch (ExprClass()) {
+      case prefix:
+         if (type==openparenthesis || type==openfunction)
+            return false;
+         S(b);
+         if (b.constant)
+            constant = true;
+         if (b.approx)
+            approx = true;
+         break;
+      case vallary:
+         S(b);
+         if (type==parenthesis && parenthesis.type==parenthesis)
+            Unlink();
+         else {
+            if (b.constant)
+               constant = true;
+            if (b.approx)
+               approx = true;
+         }
+         break;
+      case postfix:
+         S(a);
+         if (a.constant)
+            constant = true;
+         if (a.approx)
+            approx = true;
+         break;
+      case binary:
+         S(a);
+         S(b);
+         if (a.constant && b.constant)
+            constant = true;
+         if (a.approx || b.approx)
+            approx = true;
+         break;
+      case nary:
+         constant = true;
+         approx = false;
+         for (iter:arg) {
+            S(iter);
+            if (!iter.constant)
+               constant = false;
+            if (iter.approx)
+               approx = true;
+         }
+         if (!dict.f.Sanitize(this))
+            return false;
+         break;
+      case nullary:
+         if (type!=v)
+            constant = true;
+         if (type==f)
+            approx = true;
+         break;
+   }
+   #undef S
+   return true;
+}
diff --git a/samples/eC/ecas/misc.ec b/samples/eC/ecas/misc.ec
new file mode 100644 (file)
index 0000000..0958420
--- /dev/null
@@ -0,0 +1,148 @@
+import "ecere"
+public:
+
+//TODO:  Make this use a tree instead of a linear lookup
+class Dictionary {
+   uint lookup(const char *str, bool create) {
+      uint ret = 0;
+      for (i:array) {
+         if (!strcmp(i, str))
+            return ret;
+         ret++;
+      }
+      if (!create)
+         return (uint)-1;
+      array.Add(StrDup(str));
+      return ret;
+   }
+   const char *name(uint index) {
+      if (index >= array.size)
+         return null;
+      return array[index];
+   }
+   ~Dictionary() {
+      for (i:array)
+         delete i;
+   }
+private:
+   Array<char*> array {};
+};
+
+//CharFlags is a collection of characters that are to be returned (or not returned) by the find* functions
+class CharFlags {
+   property char *charsSet {
+      set {
+         const byte *n = (const byte*)value;
+         byte val = 0;
+         while (*n)
+            flags[(uint)*n++] = ++val;
+      }
+   }
+   /*property char * {
+      set { charsSet = value; }
+   }*/
+   
+   byte flags[256]; //flags[c] indicates whether character c is selected
+   void Clear(void) {
+      memset(flags, 0, sizeof(flags));
+   }
+   void Set(const char *needles, int val) {
+      const byte *n = (const byte*)needles;
+      byte v = (byte)val;
+      while (*n)
+         flags[(uint)*n++] = v;
+   }
+};
+
+char *findfirst(const char *haystack, uint haystacklen, CharFlags cf) {
+   const byte *h = (const byte*)haystack;
+   while (haystacklen--) {
+      if (cf.flags[(uint)*h++])
+         return h-1;
+   }
+   return null;
+}
+char *findfirstnon(const char *haystack, uint haystacklen, CharFlags cf) {
+   const byte *h = (const byte*)haystack;
+   while (haystacklen--) {
+      if (!cf.flags[(uint)*h++])
+         return h-1;
+   }
+   return null;
+}
+char *findlast(const char *haystack, uint haystacklen, CharFlags cf) {
+   const byte *h = (const byte*)haystack + haystacklen;
+   while (haystacklen--) {
+      if (cf.flags[(uint)*--h])
+         return h;
+   }
+   return null;
+}
+char *findlastnon(const char *haystack, uint haystacklen, CharFlags cf) {
+   const byte *h = (const byte*)haystack + haystacklen;
+   while (haystacklen--) {
+      if (!cf.flags[(uint)*--h])
+         return h;
+   }
+   return null;
+}
+
+char *StrDup(const char *src) {
+   char *ret;
+   uint len;
+   if (!src)
+      src = "";
+   len = strlen(src)+1;
+   ret = new char[len];
+   memcpy(ret, src, len);
+   return ret;
+}
+
+define NB_ALLOWHIGHERDIGITS = 256;
+define NB_ALLOWCAPLETTERS = 512;
+define NB_ALLOWLCASELETTERS = 1024;
+define NB_ALLOWLETTERS = (NB_ALLOWCAPLETTERS | NB_ALLOWLCASELETTERS);
+
+define NB_Dec      = ((uint16)(10));
+define NB_Hex      = ((uint16)(16 | NB_ALLOWLETTERS));
+define NB_Bin      = ((uint16)(2));
+define NB_Oct      = ((uint16)(8));
+define NB_OctLoose = ((uint16)(8 | NB_ALLOWHIGHERDIGITS));
+
+enum ReadULLError {noerror=0,empty=1,overflow=2};
+
+//input must only contain valid digits for the given base
+unsigned long long ReadULL_Valid(const char *ptr,const char *eptr, uint16 base, ReadULLError *errorout)
+{
+       unsigned long long ret=0;
+       unsigned long long multiplier=1;
+       unsigned long long temp;
+       ReadULLError rerror = noerror;
+       if (ptr>=eptr)
+       {
+               rerror = empty;
+               goto rn_end;
+       }
+       //the range ptr-->eptr is all valid digits, and eptr is iterating backwards through the range.
+       while (eptr-->ptr)
+       {
+               char c=*eptr;
+               //this series of if statements takes advantage of the fact that 'a'>'A'>'0'
+               if (c>='a')
+                       c-='a'-10;
+               else if (c>='A')
+                       c-='A'-10;
+               else
+                       c-='0';
+               temp=c;
+               temp*=multiplier;
+               if ((c&&!temp) || ret > ~temp) //if overflow occurred
+                       rerror = overflow;
+               ret+=temp;
+               multiplier *= (unsigned long long)base & 0xFF;
+       }
+rn_end:
+       if (errorout)
+      *errorout = rerror;
+       return ret;
+}
diff --git a/samples/guiAndGfx/curvyweb/curvyweb.ec b/samples/guiAndGfx/curvyweb/curvyweb.ec
new file mode 100644 (file)
index 0000000..dea4d56
--- /dev/null
@@ -0,0 +1,174 @@
+// CurvyWeb -- By Joey Adams
+import "ecere"
+
+class Form1 : Window
+{
+   text = "Form1 Professional Edition";
+   background = activeBorder;
+   borderStyle = sizable;
+   hasMaximize = true;
+   hasMinimize = true;
+   hasClose = true;
+   size = { 400, 400 };
+   anchor = { horz = -19, vert = 44 };
+
+   int webDensity;
+   webDensity = 10;
+
+   Slider slider { this, size = { 25, 350 }, position = {375, 25}, direction=vertical, percent=(double)webDensity/200.0;
+
+      bool NotifySlide(Slider slider, double percent)
+      {
+         webDensity = (int)(percent*200);
+         Update(null);
+         return true;
+      }
+   };
+
+   void OnResize(int width, int height)
+   {
+      slider.position.x = width-25;
+      slider.size.h = height-50;
+   }
+
+   void OnRedraw(Surface surface)
+   {
+      int i,w,h,d;
+      w = this.clientSize.w-1;
+      h = this.clientSize.h-1;
+      d = webDensity;
+      if (d<=0)
+         d = 1;
+      for (i=0; i<=d; i++)
+         surface.DrawLine(0,i*h/d,i*w/d,h);
+   }
+   
+   /*void OnRedraw(Surface surface)
+   {
+      int i,w,h,d;
+      w = this.clientSize.w-1;
+      h = this.clientSize.h-1;
+      d = webDensity;
+      if (d<=0)
+         d = 1;
+      for (i=0; i<=d; i++) {
+         surface.DrawLine(i*w/d,0,i*w/d,h);
+         surface.DrawLine(0,i*h/d,w,i*h/d);
+      }
+   }*/
+}
+
+//Ar, ye slider be pirated from Acovel Player!
+
+class Gadget : Window
+{
+   clickThrough = true;
+   inactive = true;
+
+   bool OnLeftButtonDown(int x, int y, Modifiers mods)
+   {
+      
+      return true;
+   }   
+};
+
+define skinBackground = Color { r = 40, g = 40, b = 40 };
+define skinForeground = white;
+
+class Slider : Gadget
+{
+   ScrollDirection direction;
+   opacity = 0;
+   bool dragging;
+   double percent;
+   int offset;
+
+   void OnRedraw(Surface surface)
+   {
+      if(direction == horizontal)
+      {
+         int pos = (int)((clientSize.w-7) * percent + 0.5) + 3;
+         surface.SetBackground(skinForeground);
+         surface.Area(0, clientSize.h / 2 - 3, clientSize.w-1, clientSize.h/2 + 3);
+         surface.SetForeground(black);
+         surface.Rectangle(1, clientSize.h / 2 - 2, clientSize.w-2, clientSize.h/2 + 2);
+          
+         surface.SetBackground(darkGray);
+         surface.Area(pos - 3, 0, pos + 3, clientSize.h-1);
+         surface.SetForeground(white);
+         surface.Rectangle(pos - 3, 0, pos + 3, clientSize.h-1);
+         surface.VLine(2, clientSize.h-3, pos);
+      }
+      else
+      {
+         int pos = (int)((clientSize.h-7) * (1.0-percent) + 0.5) + 3;
+         surface.SetBackground(skinForeground);
+         surface.Area(clientSize.w / 2 - 3, 0, clientSize.w/2 + 3, clientSize.h-1);
+         surface.SetForeground(black);
+         surface.Rectangle(clientSize.w / 2 - 2, 1, clientSize.w/2 + 2, clientSize.h-2);
+
+         surface.SetBackground(darkGray);
+         surface.Area(0, pos - 3, clientSize.w-1, pos + 3);
+         surface.SetForeground(white);
+         surface.Rectangle(0, pos - 3, clientSize.w-1, pos + 3);
+         surface.HLine(2, clientSize.w-3, pos);
+      }
+   }
+
+   bool OnLeftButtonDown(int x, int y, Modifiers mods)
+   {
+      dragging = true;
+      Capture();
+      OnMouseMove(x, y, mods);
+      return true;
+   }
+
+   bool OnMouseMove(int x, int y, Modifiers mods)
+   {
+      if(dragging)
+      {
+         if(direction == horizontal)
+            percent = ((double)x - offset - 3) / (double)(clientSize.w-7);
+         else
+            percent = 1.0-((double)y - offset - 3) / (double)(clientSize.h-7);
+         // TO FIX: Max(Min didn't work with double
+         if(percent < 0) percent = 0;
+         else if(percent > 1) percent = 1;
+         Update(null);
+         NotifySlide(master, this, percent);
+      }
+      return true;
+   }
+
+   void OnMouseCaptureLost()
+   {
+      OnLeftButtonUp(0,0,0);
+   }
+
+   bool OnLeftButtonUp(int x, int y, Modifiers mods)
+   {
+      if(dragging)
+      {
+         dragging = false;
+         ReleaseCapture();
+         NotifySlideUp(master, this, percent);
+      }
+      return true;
+   }
+
+public:
+   virtual bool Window::NotifySlide(Slider slider, double percent);
+   virtual bool Window::NotifySlideUp(Slider slider, double percent);
+   property double percent
+   {
+      get { return percent; }
+      set { if(!dragging) { percent = value; Update(null); } }
+   }
+   property ScrollDirection direction
+   {
+      set { direction = value; }
+      get { return direction; }
+   };
+}
+
+Form1 form1 {};
diff --git a/samples/guiAndGfx/curvyweb/curvyweb.epj b/samples/guiAndGfx/curvyweb/curvyweb.epj
new file mode 100644 (file)
index 0000000..856059a
--- /dev/null
@@ -0,0 +1,43 @@
+{
+   "Version" : 0.2,
+   "ModuleName" : "bezier",
+   "Options" : {
+      "Warnings" : "All",
+      "TargetType" : "Executable",
+      "Libraries" : [
+         "ecere"
+      ]
+   },
+   "Configurations" : [
+      {
+         "Name" : "Debug",
+         "Options" : {
+            "Debug" : true,
+            "Optimization" : "None",
+            "PreprocessorDefinitions" : [
+               "_DEBUG"
+            ],
+            "TargetFileName" : "bezier",
+            "TargetDirectory" : "debug",
+            "ObjectsDirectory" : "debug"
+         }
+      },
+      {
+         "Name" : "Release",
+         "Options" : {
+            "Debug" : false,
+            "Optimization" : "Speed",
+            "TargetFileName" : "bezier",
+            "TargetDirectory" : "release",
+            "ObjectsDirectory" : "release"
+         }
+      }
+   ],
+   "Files" : [
+      "curvyweb.ec"
+   ],
+   "ResourcesPath" : "",
+   "Resources" : [
+
+   ]
+}
\ No newline at end of file