Garnet - A Ruby Like Language

Introduction

In Garnet, every thing you manipulate is an object, and the results of those manipulations are objects. All the objects are dervied from a class named Object that has some pre-defined methods.

Methods are invoked by sending a message to an object. The message contains the method's name, along with any parameters the method may need. When an object receives a message, it looks into its own class for a definition of the named method. If found, that method is executed. If the method isn't found, it passes the method invocation to its superclass. If, when reaching the Ojbect class, a method call has no corresponding definition, the NoMethodError exception is raised.

Note that even integers, floating point numbers are objects as well. They are objects of class Integer and Float respectively. For example

     -1234.abs()
is a valid expression and returns 1234.

Object Heirarchy

You need to have the following object hierarchy pre-defined. A class defined in the main program becomes a new class in this heirarchy derived from Object class.
        Object
          |
          |
   /---------------------------------------------------\
   |      |     |       |       |      |               |
Integer Float  String Array   Hash   Exception   User-defined-class

The following functions are pre-defined in Object class.

defined?(expr)

Return false if the expr expression is not defined. Otherwise returns true.

print(expr)

Prints the expression given to it as parameter

gets

Reads a string from standard input.

Nested Classes

Class definitions can be nested. A class defined in another class is available only with in that class.

Class definition

Syntax:
    class identifier
      expr..
    end
defines a new class derived from the class Object. Class names are identifiers beginning with uppercase character.

    class identifier < superclass
      expr..
    end
defines a new class derived from the class superclass.

Method definition

Examples:
    def fact(n)
      if n == 1 then
         1 
      else
        n * fact(n-1)
      end
    end
Syntax:
    def method_name `(' [arg, ... ] `)'
      expr..
    end
Defines a new method. The method_name should be an identifier. Notice the method is not available before the definition. For example:
    foo
    def foo
      print "foo\n"
    end
will raise an exception for undefined method invoking.

Method definitions can not be nested.

The return value of the method is the value given to the return expression, or that of the last evaluated expression.

Lexical Structure

The character set used in the Garnet source files is ASCII. The case of characters in source files is significant. All syntactic constructs except identifiers may be separated by an arbitrary number of whitespace characters and comments. The whitespace characters are space, tab, vertical tab, backspace, carriage return, and form feed. You can assume that expressions are written in a single line.

Identifiers

Garnet identifiers consist of alphabetic and numeric characters and underscore, and begin with non-numeric characters (including underscore). The length of identifiers is restricted to 32 characters.

Examples:

    foobar
    ruby_is_simple
    _var_with_under_score

Identifiers ending with '?' are considered as predicate functions (functions returning true or false).

Comments

Garnet comments start with "#" outside of a string. All the following text until the end of the line should be ignored.

Examples:

    # this is a comment line

Program

Garnet programs are sequence of expressions. Each expression is delimited by a semicolon(;) or newline.

Note: An expression is written in a single line. In our language, there is no way to extend the experssion to next line.

String Literals

String expressions begin and end with double or single quote marks. Double-quoted strings can include to backslash escaped characters. Single-quoted strings are taken literally with out any exceptions.

The following backslash escaped characters have special meanings in double quoted strings.

\t tab(0x09) 
\n newline(0x0a) 
\r carriage return(0x0d) 
\f form feed(0x0c) 
\b backspace(0x08) 
\a bell(0x07) 
\s whitespace(0x20) 

Examples:

    "this is a string\n"
        will print as
    >this is a string expression
    >

    'this is a single quoted \t string'
        will print as
    >this is a single quote \t string

Numeric literals

123         integer 
-123        integer(signed) 
1_234       integer(underscore within decimal numbers ignored) 
123.45      floating point number 
1.2e-3      floating point number 
0xffff      hexadecimal integer 
0b01011     binary integer 
0377        octal integer 

Variables

Variable names must begin with a lower case alphabetic character. The maximum variable name length in our language is 32. The first assignment in the local scope (bodies of class, method definition) to an identifier declares the local variable. Undeclared identifiers are assumed to be method invocations without arguments.

Local variables assigned for first time in a block are only valid in that block. They are called `dynamic variables.' For example:

    i0 = 1
    loop {
      i1 = 2
      print defined?(i0), "\n"  # true
      print defined?(i1), "\n"  # true
      break
    }
    print defined?(i0), "\n"    # true
    print defined?(i1), "\n"    # false

Constants

Constants are special variables that can be assigned exactly once. Constant names must begin with an upper case alphabetic character.

Array expression

Examples:
    [1, 2, 3]
Syntax:
    `[' expr,...`]'
Returns an array, containing elements having the values specified by the comma-separated expressions. Arrays are instances of the class Array.

Hash expression

(Extra Credit) Examples:
    {1=>2, 2=>4, 3=>6}
Syntax:
    { expr => expr...}
Returns a new Hash object, which maps each key to corresponding value. Hashes are instances of the class Hash.

Method Invocation

Methods are invoked with the notation object.method(parameters). If the function takes no parameters, the parentheses are optional.
    foo.bar()
    foo.bar
    print("hello world\n")
Note that print("hello") and "hello".print() refer to the invocation of method 'print' of "hello" string object.

Assignment

Syntax:
    variable '=' expr
Examples:
    foo = bar
    fooarray[0] = bar

Operators and Precedence

    high   ::
           []
           -(unary)  +(unary) !
           *  / 
           +  -
           >  >=  <  <=
           ==
           &&
    low    ||

Control Structures

if
Syntax:
    if expr then
      expr...
    [elsif expr then
      expr...]...
    [else
      expr...]
    end
if expressions are used for conditional execution. The values false and nil are considered to be false, and every other value is considered to be true. Notice Ruby uses the keyword elsif, not else if nor elif.

|| and &&
Syntax:

    expr `||' expr
    expr `&&' expr

`||' expression evaluates the left hand side, then if the result is false, evaluates the right hand side and returns its value. Similarly, `&&' expression evaluates left hand side, then if the result is true, it evaluates and returns the value of the right hand side.

!
Syntax:

    `!' expr
Returns true if expr is false, and false if expr is true.

while
Syntax:

    while expr do
      ...
    end
Executes the body repeatedly while expr returns true.

break
Syntax:

    break
Exits from the most internal loop.

next
Syntax:

    next
Jumps to next iteration of the most internal loop.

For
Syntax:

    for lhs... in expr do
      expr..
    end
Executes body for each element in the result of array expression expr.

Example:

    for i in [1, 2, 3]
        print i*2, "\n"
    end

return

Examples:

    return
    return 12
Syntax:
    return [expr[`,' expr...]]
Exits from method with the return value. If two or more expressions are given, an array containing these values will be the return value. If no expression given, the result value is nil.