N9I - 5/13/2010

Written on Wed, 12 May 2010 23:14:50 +0000 - Last updated on Sat, 12 Jun 2010 03:48:07 +0000A journal entry in the development of the N9I programming language, dated May 12, 2010

An attempt to describe syntax

This is an attempt, using code examples, to describe as much Naitekiakki ("N9I") language syntax as possible. Each main-level (largest) heading represents one project, while each secondary-level (smaller) heading represents a complete file, with location relative to some hypothetical project root. These hypothetical projects have no relationship to each other. This is important because folder structure indicates program module structure. If a class, module, or package referenced in this code is not defined within this note, assume it comes from the standard language API; do note, however, that there is no final definition of an API yet, so take that too as a hypothetical.

A command-line Pokedex utility

This is a small command-line utility that reads in Pokemon data from XML files located in a directory called pokemon and formats them.

main.n9i

// Import statement was sort of taken from Python here from pokemon import Pokemon; import IO; // Notice that the main function is not inside a class. // Files represent modules, which can contain functions, classes, variables, and constants. public void main(string[] args) { if(args.length > 1) { // IO.Console is a Provider (see N9I - 2/2/2010) "Usage: pokedex [number|name]" -> IO.Newline -> IO.Console; } else { Pokemon pk; // is keyword: tests a variable for a type. // Yes, technically args[0] is a string // but assume the string class has some method // that can automatically cast to an integer. // In Naitekiakki we are usually concerned with how an object acts // not what the actual type of the object is. // Feel free to read "is" as "can act as", then. if(args[0] is int) { pk = (int) args[0]; } else { pk = args[0]; } // A Big Block Quote (BBQ) string is a string contained within brackets. // The contents of the BBQ string may contain another bracket pair, but not a lone close bracket. // If a lone bracket is needed, it must be escaped: \{ \}. No other characters need escaping. Line breaks are preserved. // If the entire string is indented, the indents are removed; this is so that strings can be included inside indented code. // Trailing whitespace is removed. Letters may be added in between the % and the opening brace in order to apply certain properties to a string; the letter "i" signifies variable interpolation. // A BBQ string cannot be used with the at operator. (%i{ =Pokemon ${pk.number}= Name: ${pk.name} Species: ${pk.species} Type: ${pk.type1}/${pk.type2} Description: ${pk.description} }) -> IO.Console; } }

pokemon.n9i

from XML import XMLDocument; /** usage: Pokemon pk = 25; pk.name -> IO.Newline -> IO.Console; // "Pikachu" pk.species -> IO.Newline -> IO.Console; // "Mouse" (pk + 1) -> IO.Newline -> IO.Console; // 26 Pokemon pk2 = (pk + 1); pk -> IO.Newline -> IO.Console; // "Raichu"

*/ public class Pokemon { private string name; private int number; private Type type1; private Type type2; private string species; private string description; private Pokemon() { // We cannot create the pokemon class like this } // Casting method (see N9I - 2/5/2010) // Pokemon pk = 1; is transformed into: Pokemon pk = Pokemon.getPokemonByNumber(1); // as is: (Pokemon) 1 // or calling a function foo(Pokemon pk); like so: foo(1) [@from int] public static Pokemon getPokemonByNumber(int pokeNum) { // @"pokemon/index.list" is a Resource (N9I - 2/2/2010) // This is a file containing a list of pokemon names. It is automatically turned into a string array string[] pokemonNames = @"pokemon/index.list"; return Pokemon.getPokemonByName(pokemonNames[pokeNum - 1]); } [@from string] [@validate name adjust name.toLowerCase()] public static Pokemon getPokemonByName(string name) { // Variable interpolation // Resource objects can act as a boolean value. The truth value of a resource is true // if the resource exists (even if it is empty) and false if it does not. if(@"pokemon/${name}.xml") { return Pokemon.getPokemonFromXML(@"pokemon/${name}.xml"); } else { throw new IllegalArgumentException("Not a Pokemon name"); } } /** * This method assumes that the Pokemon XML file looks like: * <pokemon> * <name>Bulbasaur</name> * <number>1</number> * <types> * <type1>Grass</type1> * <type2>Poison</type2> * </types> * <species>Seed</species> * <description>Bulbasaur is a seed Pokemon. It yadda yadda yadda...</description> * </pokemon> **/ [@from XMLDocument] private static Pokemon getPokemonFromXML(XMLDocument doc) { Pokemon pk = new Pokemon(); // This is interesting. getElementsByTagName() naturally returns DOM elements, not strings. // Many, if not all, N9I objects have some sort of string representation, and quite many of them // can be converted from a string. In this case, the string representation of a DOM element is the text it contains. // Furthermore, the string interface itself has various cast methods, one of which returns an integer. So the // string obtained from the DOM element can then be turned into an integer. // The Type class (see below) does not implement the string interface but it nonetheless acts like a string. // Therefore, as with the integer, when the DOM elements type1 and type2 are made into strings, those strings can then be made into Types. pk.name = doc.getElementsByTagName("name")[0]; pk.number = doc.getElementsByTagName("number")[0]; pk.type1 = doc.getElementsByTagName("types")[0].getElementsByTagName("type1")[0]; pk.type2 = doc.getElementsByTagName("types")[0].getElementsByTagName("type2")[0]; pk.species = doc.getElementsByTagName("species")[0]; pk.description = doc.getElementsByTagName("description")[0]; return pk; } // Getters (see N9I - 2/4/2010). These are another shorthand; when pk.name is requested, // the compiler turns this into pk.getName(). This works sort of like C# properties. // These getters do not have associated setters, making them read-only. // The @get and @to annotations can be mixed, of course. [@get name] [@to string] public string getName() { return name; } [@get description] public string getDescription() { return description; } [@get number] [@to int] public int getNumber() { return number; } [@get type1] public Type getType1() { return type1; } [@get type2] public Type getType2() { return type2; } [@get species] public string getSpecies() { return species; } } // This class demonstrates how to create "string-like" objects that implement some sort of validation // transparently. For example, Type t = "Psychic"; is allowed whereas Type t = "Light"; throws an exception // since "Light" is not an allowed Type. See N9I - 2/8/2010for the note on validation. // This particular example might seem like a glorified enum but there are other potential applications here as well. // For example, a class called URL might implement a @from string method that validates a string for a well-formed URL. // Hence URL url = "http://google.com"; passes while URL url = "dsfargeg!"; does not. Since the URL class can be used // as a regular string (and vice versa), it is possible to use it in a method signature to ensure only valid URLs can be passed in. public class Type { private const string[] ALLOWED_TYPES = ["Grass","Bug","Poison","Fire","Water", "Electric","Psychic","Dark","Steel", "Fighting","Flying","Dragon","Ghost", "Normal","Rock","Ground","Ice"]; private string value; private Type() { // can't construct new Types } [@from string] [@validate str test ALLOWED_TYPES.contains(str)] public static Type getType(string str) { value = str; } [@to string] public string toString() { return value; } }