Follow

One of the first in-class examples for my programming language class is a program that generates numbers, following a grammar from Scott, Programming Language Pragmatics.

In , this is 10 tiny functions, 49 lines including lots of PEP 8 whitespace:

import random

def evaluate(exp):
if isinstance(exp, str):
return exp
return exp()

def concatenate(*exps):
return ''.join([evaluate(e) for e in exps])

def choose(*exps):
return evaluate(random.choice(exps))

def star(exp):
result = ''
while random.randint(0, 1):
result += evaluate(exp)
return result

def number():
return choose(integer, real)

def integer():
return concatenate(digit, star(digit))

def real():
return choose(concatenate(integer, exponent), concatenate(decimal, choose(exponent, '')))

def decimal():
return concatenate(star(digit), choose(concatenate('.', digit), concatenate(digit, '.')), star(digit))

def exponent():
return concatenate(choose('e', 'E'), choose('+', '-', ''), integer)

def digit():
return choose('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')

print(number())

In , largely because there's no way to define a new type that subsumes String, it's an interface, 5 classes, 11 functions, 6 instance variables, 6 enum values, a huge switch statement, and 125 lines.

import java.util.Random;

interface Symbol {
public String generate();
}

class Terminal implements Symbol {

String symbol;

public Terminal(String symbol) {
this.symbol = symbol;
}

public String generate() {
return symbol;
}
}

class Concatenation implements Symbol {

Symbol[] symbols;

public Concatenation(Symbol... symbols) {
this.symbols = symbols;
}

public String generate() {
String result = "";
for (Symbol s : symbols) {
result += s.generate();
}
return result;
}
}

class Choice implements Symbol {

static Random r = new Random();

Symbol[] symbols;

public Choice(Symbol... symbols) {
this.symbols = symbols;
}

public String generate() {
return symbols[r.nextInt(symbols.length)].generate();
}
}

class Star implements Symbol {

static Random r = new Random();

Symbol symbol;

public Star(Symbol symbol) {
this.symbol = symbol;
}

public String generate() {
String result = "";
while (r.nextBoolean()) {
result += symbol.generate();
}
return result;
}
}

public enum Nonterminal implements Symbol {

NUMBER, INTEGER, REAL, DECIMAL, EXPONENT, DIGIT;

public String generate() {
switch (this) {
case NUMBER:
return new Choice(INTEGER, REAL).generate();
case INTEGER:
return DIGIT.generate() + new Star(DIGIT).generate();
case REAL:
return new Choice(
new Concatenation(INTEGER, EXPONENT),
new Concatenation(DECIMAL, new Choice(EXPONENT, new Terminal("")))).generate();
case DECIMAL:
return new Concatenation(
new Star(DIGIT),
new Choice(
new Concatenation(new Terminal("."), DIGIT),
new Concatenation(DIGIT, new Terminal("."))),
new Star(DIGIT)
).generate();
case EXPONENT:
return new Concatenation(
new Choice(new Terminal("e"), new Terminal("E")),
new Choice(new Terminal("+"), new Terminal("-"), new Terminal("")),
INTEGER
).generate();
case DIGIT:
return new Choice(
new Terminal("0"),
new Terminal("1"),
new Terminal("2"),
new Terminal("3"),
new Terminal("4"),
new Terminal("5"),
new Terminal("6"),
new Terminal("7"),
new Terminal("8"),
new Terminal("9")
).generate();
}
return null; // We should never get here
}

public static void main(String[] args) {
System.out.println(NUMBER.generate());
}
}

I used to love Java, but this amount of boilerplate really gets in the way.

Sign in to participate in the conversation
CleverLibre Social

CleverLibre Social is an inclusive social instance for open discussion, learning, and community.
All cultures welcome.
Hate speech and harassment strictly forbidden.