Summary
I have made a pre-release of HeronFront which semi-successfully translates to C++ the primitives module from the standard library.

Advertisement

I said HeronFront semi-successfully translates to C++ because, besides virtually everything else interesting, there is no function invocation by string yet. That little feature is top priority and should be ready in a couple of days. Function invocation by string makes the HeronScript interpreter easy to implement.

For the time being it may interest some to see some of the changes in the language since the last time I posted Heron code. One thing which I mentioned briefly yesterday is that standalone operators are assumed to be prefix unary operators mapping to the object instance. This allows me to eliminate the need for the this keyword. You can refer to the object instance by value through * and by address through @.

Hopefully the operator overloading scheme is obvious enough. A cool feature of Heron is that it supports a sequence of any length of valid operator characters as an overloadable operator. For instance I could overload %^&! as a prefix/unary operator by simply writing a function named: _pre_percent_hat_ampersand_bang() ( the infix/binary version drops the leading _pre).

I know some people are going to be offended by what appears to be a preponderance of leading underscores. They are reserved for certain keywords, operator overload names, and system level primitives. As a result in the standard library you see a heck of a lot of them, but user-code should be much prettier. Part of their purpose is to make abuse unattractive.

Anyway, enought blabbering here is the current version of the primitives module:

module primitives_module
{
public
{
class int
{
public {
// constructor
_init(_int x) { m = x; }
// operators
_eq(self x) { m = x.m; }
_plus_eq(self x) { m += x.m; }
_minus_eq(self x) { m -= x.m; }
_star_eq(self x) { m *= x.m; }
_slash_eq(self x) { m /= x.m; }
_percent_eq(self x) { m %= x.m; }
_pre_minus() : self { result = -m; }
_post_plus_plus() : self { result = *; ++m; }
_post_minus_minus() : self { result = *; --m; }
_pre_plus_plus() : self* { ++m; result = @; }
_pre_minus_minus() : self* { --m; result = @; }
_plus(self x) : self { result = *; result += x; }
_minus(self x) : self { result = *; result -= x; }
_star(self x) : self { result = *; result *= x; }
_slash(self x) : self { result = *; result /= x; }
_percent(self x) : self { result = *; result %= x; }
_gt(self x) : bool { result = (compare(x) > 0); }
_gt_eq(self x) : bool { result = (compare(x) >= 0); }
_lt(self x) : bool { result = (compare(x) < 0); }
_lt_eq(self x) : bool { result = (compare(x) <= 0); }
_eq_eq(self x) : bool { result = (compare(x) == 0); }
_bang_eq(self x) : bool { result = (compare(x) != 0); }
// public methods
compare(self x) : int { result = m.compare(x.m); }
to_primitive() : _int { result = m; }
}
fields {
_int m;
}
}
class char {
public {
// constructors
_init(_char x) { m = x; }
// operators
_eq(char x) { m = x.m; }
_gt(self x) : bool { result = (compare(x) > 0); }
_gt_eq(self x) : bool { result = (compare(x) >= 0); }
_lt(self x) : bool { result = (compare(x) < 0); }
_lt_eq(self x) : bool { result = (compare(x) <= 0); }
_eq_eq(self x) : bool { result = (compare(x) == 0); }
_bang_eq(self x) : bool { result = (compare(x) != 0); }
// public member functions
compare(self x) : int { result = m.compare(x.m); }
to_primitive() : _char { result = m; }
}
fields {
_char m;
}
}
class bool {
public {
// constructor
_init(_bool x) { m = x; }
// operators
_eq(bool x) { m = x.m; }
_amp_amp(self x) : self { result = m && x.m; }
_pipe_pipe(self x) : self { result = m || x.m; }
_pre_bang() : self { result = !m; }
_eq_eq(self x) : bool { result = (m == x.m); }
_bang_eq(self x) : bool { result = (m != x.m); }
// public member functions
to_primitive() : _bool { result = m; }
}
fields {
_bool m;
}
}
}
}

> How do you determine the precedence of <tt>%^&!</tt>? If > it were an infix operator and you had the expression > > foo = this + that * those %^&! mine; > > What gets done in what order if all are overloaded?

Currently all infix operators have equal precedence, and are evaluated right to left. Same with prefix operators. Prefix operators have a higher precedence than infix operators. Perhaps a default evaluation order of left to right would be more intuitive? I hope the lack of complicated precedence rules doesn't turn out to be controversial.

I'm assuming this holds for all types, so if you had (in C++ syntax, translate to Heron as appropriate).

int i = 1, j = 2, k = 3;

int l = i + j * 3;

if it is left to right, then l is 9. Mathematically, l should be 7. If that is the case, then you may have problems getting adopters, most of us are used to traditional precedence rules.

> I'm assuming this holds for all types, so if you had (in > C++ syntax, translate to Heron as appropriate). > > int i = 1, j = 2, k = 3; > > int l = i + j * 3; > > if it is left to right, then l is 9. Mathematically, l > should be 7. If that is the case, then you may have > problems getting adopters, most of us are used to > traditional precedence rules.

But what about calculators, couldn't they be considered to be traditional? Do you think people would abandon a language because it takes a simple and unamibguous approach to operator precedence? I can never remember precedence rules from one language to another, so I have adopted the habit of always using parantheses to make my code unambiguous. It disappoints me that this might be a deal killer for some people, but I do accept that it may be the reality.

From a previous thread "In important goal for me is to eventually have a standard library mathematics package which rivals specialized languages Mathematica and Maple. "

> Do you think people would abandon a > language because it takes a simple and unamibguous > approach to operator precedence? I can never remember > precedence rules from one language to another, so I have > adopted the habit of always using parantheses to make my > code unambiguous.

You can't have it both ways. Left to right processing of operators is kindergarten mathmatics. If standard mathmatics is an important goal then standard mathmatic rules have to be followed. If your language says that "1 + 2 * 3" is different to "3 * 2 + 1" then it's not simple and unambiguous, it's wrong.

Smalltalk has been using left-to-right infix operators since the mid-1970's, and there are probably more flavors of Smalltalk (commercial and free) available now than ever before -- all with the same operator-precedence.

Use parents to distinguish (a + b) * c from a + (b * c)

I think in the end it will be simplest to go with the flow and follow more-or-less the precedence for operators rules of C. Straying from convention is probably a very bad idea. I will place all uncommon operators in between assignment and comma operators. Here is the chart:

This differs significantly from C, C++, Java and C#. And it'd be rather inconvenient to have such slight differences between languages with otherwise similar syntax.

Besides, there is a reason for 'traditional' precedence of &&, || and ==, != etc. You can see the code like follows:

if ( (a == b) && (c == d) )
. . .

much more frequently than its counter-part:

if ( (a && b) == (c && d) )
. . .

I think that if like you said above, precedence does not mean much to you, I'd stick with 'traditional' rules...

Thanks for pointing that out Pavel. That was actually an unintentional error. However, I am now leaning towards simplifying the rules to a minimum which satisfies arithmetic precedence. I would welcome your comments on my latest post at: http://www.artima.com/weblogs/viewpost.jsp?thread=126323

Why on earth would anyone want an operator called "%^&!" ? It's hard to type, and harder to read than English. Wouldn't they rather call it "foo" in practice?