Metaprogramming

From Rosetta Code
Revision as of 15:06, 17 May 2011 by rosettacode>CRGreathouse (PARI)
Task
Metaprogramming
You are encouraged to solve this task according to the task description, using any language you may know.

Name and briefly demonstrate any support your language has for metaprogramming. Your demonstration may take the form of cross-references to other tasks on Rosetta Code. When possible, provide links to relevant documentation.

For the purposes of this task, "support for metaprogramming" means any way the user can effectively modify the language's syntax that's built into the language (like Lisp macros) or that's conventionally used with the language (like the C preprocessor). Such facilities need not be very powerful: even user-defined infix operators count. On the other hand, in general, neither operator overloading nor eval count. The task author acknowledges that what qualifies as metaprogramming is largely a judgment call.

E

Forms of metaprogramming existant in E:

  • Quasi-literals provide convenient notation for data structures and values for which there is not literal syntax, as discussed in String#E.
  • E program fragments may be quoted, manipulated as an AST, and evaluated, similarly to Lisp; lexical environments are first-class objects (though static with respect to the evaluated code). Demonstrated in Runtime evaluation#E and Eval in environment#E.
  • Control structures may be defined, as demonstrated in Extend your language#E.

J

J names have one of four different grammatic types: noun, verb, adverb, conjunction. Nouns and verbs are nothing special from a metaprogramming point of view. However, adverbs and conjunctions are evaluated at "parse time" and can be used to introduce expression variants. (The result of an adverb, or of a conjunction may be either a noun, a verb, an adverb or a conjunction.)

Additionally, J script blocks (a block of text terminated by a right parenthesis on a single line) can and are used with differing interpreters (which may be built into the language or user written).

The J implementation of the Y combinator could be considered an example of metaprogramming.

J's explicit definitions might also be considered an example of metaprogramming, since explicit definitions have data dependent syntactic types.

Lua

Due to the way Lua's syntactic sugar is designed, metatables can make some Lua code look like a Domain-Specific Language, despite technically being (mostly) just specialized operator overloading.

For example: <lang lua> class "foo" : inherits "bar" {

} </lang>

is perfectly valid syntax. (Lua does not having a built in concept of classes or inheritance.)

PARI/GP

The primary means of metaprogramming for GP is to extend it with PARI. As an example, defining an infix @ operator could work like

In src/functions/symbolic_operators/min0:

Function: _@_
Help: x@y: compute the lesser of x and y, or 0, whichever is larger.
Section: symbolic_operators
C-Name: gmin0
Prototype: GGp
Description:
 (small, small):small	 smin0ss($1, $2)
 (mp, mp):mp:prec       gmin0($1, $2, prec)
 (gen, gen):gen:prec	gmin0($1, $2, prec)

In (e.g.) basemath/somefile.c: <lang C>long smin0ss(long a, long b) {

 long c = a < b ? a : b;
 return c > 0 ? c : 0;

}


GEN gmin0(GEN a, GEN b) {

 GEN c = gcmp(a, b) < 1 ? a : b; /* copy pointer */
 return signe(c) > 0 ? gcopy(c) : gen_0;

}</lang>


Perl

You can textually transform code with a source filter, a module that when used modifies the following lines of source. Filter::Util::Call provides a general means of writing source filters. Filter::Simple is an interface to Filter::Util::Call that lets you elide a lot of boilerplate code. More important, Filter::Simple can hide the contents of quoting constructs from your filter, obviating the biggest dangers of textual metaprogramming. For example, given the following module:

<lang perl>package UnicodeEllipsis;

use Filter::Simple;

FILTER_ONLY code => sub { s/…/../g };</lang>

this program:

<lang perl>use UnicodeEllipsis;

print join(' … ', 1 … 5), "\n";</lang>

prints:

 1 … 2 … 3 … 4 … 5

Devel::Declare lets you define a new keyword by setting up a hook to be run whenever the parser encounters a bareword of your choice. Devel::Declare is powerful, but it has a reputation for being difficult to understand. See this blog post for a relatively simple usage example.

PicoLisp

As in any Lisp, metaprogramming is an essential aspect of PicoLisp. In most cases normal functions are used to extend the language (see Extend your language#PicoLisp), read-macros operate on the source level, and also runtime macros are used occasionally.

PostScript

PostScrpt allows the reification of stack, scoping (Dynamic scoping is default, but lexical scoping can be implemented using immediate loading), bindings using dictionaries, and even control flow. Here is an example of implementation of if statement

Library: initlib

<lang postscript>

/ift { 4 dict begin

   [/.if /.then] let*
   count array astore /.stack exch def
   /_restore {clear .stack aload pop}.
   .stack aload pop .if {
      _restore .then
   } {
      _restore
   } ifelse

end}. </lang> The standard if expression in postscript does not take a predicate. Instead it acts on the boolean value on top of the stack. This newly created word allows us to do <lang postscript> >| 2 {1 gt} {=} ift 2 </lang> Instead of

<lang postscript> >| 2 dup 1 gt {=} ift 2 </lang>

Note that even the let expression was implemented using meta programming <lang postscript> /let* {reverse {exch def} forall}. </lang>

Python

Metaprogramming is frowned on in Python and considered un-pythonic. The only widely known example of metaprogramming in Python was an implementation of a goto (and comefrom) keyword done as an April-fools joke.

REXX

REXX doesn't allow for the changing or overriding of syntax per se, but any of built-in functions can be overided by just specifying your own.

REXX allows the programmer to set the precision of the numbers it uses in calculations and expressions, as well as the FUZZ settings, which effects how numbers are compared (for equalness, greater or equal to, less or equal to, etc).

Tcl

Metaprogramming is considered to be normal in Tcl; the whole language was designed to support new operations that work with a similar level of integration to existing commands (and indeed, the standard commands are not syntactically special in any way), and the upvar and uplevel commands are specifically for this sort of use. Moreover, there are no language keywords that need to be worked around; words/tokens can be used to mean anything necessary. For example: <lang tcl>proc loopVar {var from lower to upper script} {

   if {$from ne "from" || $to ne "to"} {error "syntax error"}
   upvar 1 $var v
   if {$lower <= $upper} {
       for {set v $lower} {$v <= $upper} {incr v} {
           uplevel 1 $script
       }
   } else {
       # $upper and $lower really the other way round here
       for {set v $lower} {$v >= $upper} {incr v -1} {
           uplevel 1 $script
       }
   }

}</lang> The above creates a new loopVar command that might be used like this: <lang tcl>loopVar x from 1 to 4 {

   loopVar y from $x to 6 {
       puts "pair is ($x,$y)"
       if {$y >= 4} break
   }

}</lang> Which prints this:

pair is (1,1)
pair is (1,2)
pair is (1,3)
pair is (1,4)
pair is (2,2)
pair is (2,3)
pair is (2,4)
pair is (3,3)
pair is (3,4)
pair is (4,4)

As you can see, the new looping command is wholly integrated with the rest of the Tcl language.

Code generation is also possible. The easiest approach is to use the list command to generate substitution-free command, leaving all substitutions to places that are written by the programmer directly.

Finally, the total lack of keywords is exemplified by this classic fragment of Tcl: <lang tcl>set set set</lang> In this, the first set is a command (that updates a variable), the second is the name of a variable in the current namespace, and the third is a string that will be placed in a variable.