Sage
Tusk Language
Welcome to Sage
Volume (25%) Hide Volume
Topics
Improvements Over MiniCalc
Tusk offers many improvements over MiniCalc.

Delphi Synergy

Overall, Tusk is much more similar to Delphi than MiniCalc is, as discussed in many of the following sections.

Early Analysis

Perhaps the single most important difference between Tusk and MiniCalc is that Tusk offers parse-time checking of numerous issues that MiniCalc checks only at runtime.

For example, if an identifier is misspelled, or a function is called with the wrong number of arguments, Tusk catches these problems before running any code. MiniCalc finds these issues only if/when the program attempts to execute the deficient statement.

Type Safety

In MiniCalc, everything is a Variant. In Tusk, this is also true, at least at runtime. However, at parse time, Tusk has a robust type system…
  • Functions declare their return type.
  • Procedure / function arguments specify a data type.
  • Constants and variables are of a declared data type.
  • Implicit type casts are safe (such as Char to string or Integer to Int64), while unsafe conversions require an explicit function call (such as string to TDateTime or Double to Byte).

Note: Tusk offers type inference, much like Delphi does, so it isn't necessary to declare the type of every const or var.

Static Scoping

MiniCalc uses dynamic scoping, while Tusk and Delphi use static (lexical) scoping. For example, consider this MiniCalc code…

const a = 10; function f; begin @Writeln(a); end; function Demo; const a = 99; begin f(); end; Demo();

The above code prints 99. In contrast, the following (nearly identical) Delphi code prints 10

const a = 10; procedure f; begin Writeln(a); end; procedure Demo; const a = 99; begin f; end; Demo;

The above Delphi code also works unchanged in Tusk, and prints the same value.

Undefined Identifiers

In MiniCalc, reading an undefined variable yields Unassigned, which quietly converts to zero / false / empty string / etc.

Similarly, writing to an undefined variable simply defines the variable (though not when in a function).

Tusk has neither of these downsides: all variables must be declared before they are used.

Accessing Topmost Identifiers

Tusk offers a dedicated operator, unary /, for accessing a topmost identifier. MiniCalc has no such facility, but uses a convention of naming global functions and constants with a prefix of @. This is rather messy, as it makes MiniCalc code look different from Delphi code…

x := @Sqrt(@Abs(x) - @Ln(y) * @Pi);

x := Sqrt(Abs(x) - Ln(y) * Pi);

If the global Pi was hidden, MiniCalc has no solution…

const @Pi = '...'; x := @Sqrt(@Abs(x) - @Ln(y) * @Pi); // can't access the real @Pi

Meanwhile, Delphi code could do this…

const Pi = '...'; x := Sqrt(Abs(x) - Ln(y) * System.Pi);

In Tusk, the / operator is used to access the topmost definition of an identifier (which may or may not be global)…

const Pi = '...'; x := Sqrt(Abs(x) - Ln(y) * /Pi);

This symbol was chosen, among other reasons, because it often denotes "top level" or "root".

Optional Parentheses

Tusk, like Delphi, does not require an empty pair of parentheses to call a procedure or function with zero arguments. MiniCalc does require the empty parentheses for global functions, but not for methods.

Operators

In MiniCalc, there are many differences with Delphi related to operators.

Novel Operators

MiniCalc defines a bewildering assortment of operators, around 35 more than Delphi. These do little to improve the coding experience in MiniCalc, and (when used) are significant barriers to Delphi compatibility.

Tusk defines only three operators not present in Delphi:

One of the most problematic operators in MiniCalc is the comma operator, which is so pernicious that MiniCalc eventually defined a way to disable it. Tusk does not offer this operator.

Operator Precedence

MiniCalc improves upon Delphi's operator precedence, but this comes at the expense of Delphi compatibility.

MiniCalc offers 34 true operators that do not exist in Delphi (such as #and, +=, ?=, and <=>), changes the meaning of the & operator, changes the precedence of several other operators, and changes the ^ operator from postfix to prefix. Overall, MiniCalc offers a dizzying number of additional operators, precedence levels, and deviations from Delphi, all of which is carefully documented.

Meanwhile, Tusk's operators much more closely match Delphi's operators, leading to greater compatibility and less confusion.

Mixing Boolean and Relational Operators

One specific area of concern is mixing and / or operators with relational operators (=, <>, <, etc).

For example, in MiniCalc, these two are equivalent…

// These are equivalent (nice!) @Writeln(a = b or c = d); @Writeln((a = b) or (c = d));

However, in Delphi, these are equivalent…

// These are equivalent (yuck!) Writeln(a = b or c = d); Writeln((a = (b or c)) = d);

Thus, the expression a = b or c = d is legal in both MiniCalc and Delphi, but has two different meanings.

Tusk improves upon MiniCalc by using the same operator precedence as Delphi, for increased compatibility. At the same time, Tusk forbids problematic "under-parenthesized" expressions that Delphi permits (more details here).

Pointer Operators

MiniCalc uses the & operator to take the address of an expression. Tusk and Delphi use the @ operator to take addresses, and the & operator to escape keywords/operators.

All three languages use the ^ operator to dereference a pointer. However, in MiniCalc this is a prefix operator; in Tusk and Delphi, it is postfix.

Tusk uses the ** operator for exponentiation, whereas MiniCalc uses ^.

Fewer Reserved Words

MiniCalc defines several keywords/operators that are not keywords in Delphi: bitand, bitnot, bitor, bitxor, defer, false, inceptive, null, true, unassigned, and using.

In Tusk, none of the above are keywords; Tusk defines four additional keywords: Break, Continue, Exit, and out. Their status as keywords can easily be circumvented using the & operator, which works just as in Delphi.

Also, Tusk treats at and on as keywords, which Delphi technically does not, however Embarcadero's documentation states…

The words
at
and
on
also have special meanings, and should be treated as reserved words.

Therefore, at and on are keywords in Tusk. Again, the & operator easily addresses this issue.

Functions vs Procedures

Tusk distinguishes between functions (which return a value) and procedures (which do not); MiniCalc does not make this distinction.

The Result Variable

Tusk supports the Result variable in functions; MiniCalc does not.

Ternary Operator

Tusk supports Delphi's new ternary operator

Writeln(if Qty > 100 then 'Large' else 'Small');

Note that Tusk does not support the C-style ternary operator: condition ? yes : no.

More Data Types

Tusk directly supports more Delphi data types than MiniCalc…

Generics

Tusk has much better support for generics than MiniCalc. In MiniCalc, you can call @NewList or @NewIntegerList, but you cannot instantiate an IList<Boolean> or an IList<IStringList>.

In Tusk, you can use and instantiate generic types, including types that may not be compiled into the host Delphi application. For example, IList<string> is always compiled in, but IList<Single> may not be. Either way, you can work with IList<Single> in Tusk. In fact, you can do this kind of thing…

type TMyEnum = (Choice1, Choice2, Choice3, Choice4, Choice5); var e: TMyEnum := Choice2; var ls: IList<TMyEnum> := NewListOf(TMyEnum); ls.Add(e);

We plan to improve the syntax for instantiating generic lists, from the above to TDSList<TMyEnum>.Create.

Enums

Tusk supports enums (scoped and non-scoped) the way Delphi does: they are integers when stored in a Variant.

In contrast, MiniCalc stores enums as strings, leading to numerous compatibility issues (for example, MiniCalc performs ordered comparisons on enums based on name, while Delphi and Tusk use the ordinal value instead).

Tusk supports enums in common RTL functions, including Ord, Inc, Dec, Succ, Pred, etc.

Sets

Tusk supports Dephi's native set type (sets of enum, Byte, Int8, and AnsiChar). Tusk supports the familiar set operators: +, -, *, in, as well as the Include and Exclude routines. Naturally, the forin loop supports sets.

MiniCalc emulates a set of enum with a list of string.

Dynamic Arrays

Tusk directly supports Delphi's dynamic arrays. In contrast, MiniCalc arrays are variant arrays, which are less efficient (because they use the COM memory manager, not Delphi's FastMM) and don't have the same semantics. For example, Variant arrays are copied by value, but Delphi and Tusk dynamic arrays are copied by reference.

Also, Variant arrays require the elements to be OLE Automation-compatible, which rules out types like sets and records, and converts Delphi string and AnsiString values to WideString, which use the slower COM memory manager and are not reference-counted.

Records

Tusk directly supports record types, such as Decimal, TBitVector, TPoint, etc.

In MiniCalc, Decimal is supported via its custom variant type, but this omits methods and properties on the Decimal record such as Abs, IsZero, InRange, etc.

Similar issues affect many other record types, for example, TDSCharSet. If s is a TDSCharSet and ch is a Char, then Delphi and Tusk test for membership as follows…

s.Has(ch)

Meanwhile, MiniCalc requires this…

@HasValue(s, ch)

In Tusk, records such as Decimal and TDSCharSet are the real deal, becoming the custom variant type (varDecimal and varCharSet) only when cast (implicitly or explicitly) to Variant.

String Literals

Tusk does not support MiniCalc's [~ ... ~] multi-line string literal.

It does, however, support Delphi's relatively new ''' ... ''' multi-line string literal.

Tusk also offers string literals using double-quotes, but these differ from MiniCalc – they are compatible with C, C++, C#, Java, JavaScript, and JSON.

Tip

Tusk supports IPropBag literals using curly braces, making Tusk a superset of JSON (every valid JSON file is also valid Tusk, with equivalent meaning).


Of course, Tusk also supports traditional Delphi-like string literals, using single quotes and pound signs, and the newer multi-line strings that use 3 apostrophes.

Library Documentation

In MiniCalc, the library documentation is maintained manually. So, whenever a new interface method or property is exposed, whenever a new global function is defined, or a new type is added to the mix, the documentation must be updated manually.

However, Tusk offers a built-in mechanism to generate Sage documentation for all types, properties, methods, and global routines. Thus, as Tusk evolves, the documentation can be generated easily, without the risk of missing entries, copy-and-paste errors, etc.

Last Modified: 2/15 9:51:46 am
In this article (top)  View article's Sage markup
2/15 9:51:46 am