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
Qoto Mastodon

QOTO: Question Others to Teach Ourselves. A STEM-oriented instance.

An inclusive free speech instance.
All cultures and opinions welcome.
Explicit hate speech and harassment strictly forbidden.
We federate with all servers: we don't block any servers.