VGo

a Programming Language

Clinton Jeffery jeffery@cs.uidaho.edu

Draft Version 0.21, September 23, 2019.



Language Reference Manual


Abstract

VGo (pronounced "Viggo", short for "Vandal Go") is a subset of the Go programming language, from the "don't be evil" evil empire at Google, and such evil geniuses as that celebrity obvious-software-patenting researcher, Rob Pike. I am not going to say that even bigger celebrity Ken Thompson is an evil genius; he is just a genius. VGo is a tiny language intended to be implemented in a compiler construction class.





University of Idaho
Department of Computer Science
Moscow, ID 83844 USA












Contents

  1. Introduction
  2. Lexical Rules
  3. Syntax
  4. Data Types and Semantics
  5. Summary


1. Introduction

VGo is a subset of Go, a language that is trying to be a better C, with some amusing, vague, retro Pascal-ish aspects to it. VGo, the Vandal Go, is intended to correspond roughly to the subset of Go that would be covered in a CS1 class such as UIdaho's CS 120 course.

The facilities that VGo supports are not particularly new or clever. But, Go is interesting enough that students can only benefit from learning some of the features that distinguish it from the C or C++ that they may be more familiar with. The remainder of this introduction consists of a "quick tour" of VGo, based on A Tour of Go.

VGo programs are generally legal Go programs with a .go file extension. Source files have to specify what package they are in. A program begins with a main() procedure within a main package. A non-racist "Hello world" program looks like:

package main

import "fmt"

func main() {
    fmt.Println("Hello, world")
}
All legal VGo source files must be declared to be in package main. Other packages are supported only to the extent that a small number of the Go library packages are built-ins in VGo. The small number of VGo library packages is still under development; see below.

In Go, import statements and declarations can be "factored" together using parentheses as in

import (
   "fmt"
   "time"
)
This is not allowed in VGo, which only support Go's more traditional syntax:
import "fmt"
import "time"
VGo supports a small subset of the functionality of a small subset of Go's packages, including "fmt" (Println), "time" (Now), and "math/rand" (Intn). While the full Go versions of these packages support many many functions, and even types, VGo will be rather minimalist. For example, instead of defining 25 public symbols in package fmt, VGo will have as few as possible, possibly only one or two. So far, only Println().

VGo supports Go's name scope rule: only capitalized names are visible in other packages. Since user source code is all in package main, this is largely moot in VGo.

VGo supports Go's interesting function syntax, with examples such as the following. Note that the type comes after the variable, and the function return type, which is optional, comes after the parameter list.

package main

import "fmt"

func add(x int, y int) int {
    return x + y
}

func main() {
    fmt.Println(add(42, 13))
}
Go allows the type to be omitted if the next item in a comma separated parameter list is the same type. VGo also allows such omission.
func add(x, y int) int { /* legal in Go and VGo */
    return x + y
}
Go allows multiple values to be returned from a single function call. VGo does not.
...
func swap(x, y string) (string, string) { /* legal in Go, not in VGo */
return y, x
}

func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
Go supports named return values, to make things less confusing when you have a lot of return values. VGo does not.
func split(sum int) (x, y int) { /* legal in Go, not in VGo */
x = sum * 4 / 9
y = sum - x
return
}
Other than parameters, Go declares package-global and local variables using a var statement. A single var statement can accommodate a variety of types.
var c, p, j bool /* legal VGo, declares three bool variables */
Go uses variable initializers aggressively, to allow implicit type declarations. A single equals sign is used to separate multiple variable names from multiple initializer values. VGo does not do implicit types, and does not do initializers. All variables begin with their "zero value" and get assigned via assignment statements. Zero values include "false" for boolean, and "" (empty string) for strings.
var c, p, j = true, false, "no!"  /* legal in Go, not in VGo */
Go uses the so-called short assignment statement in place of var to declare a variable with implicit type. VGo does not.
  k := 3   /* legal in Go, not in VGo */
Go features 19 basic types, including "rune" and two types of complex numbers. VGo supports:
bool
string
int
float64
Go, and VGo, have no implicit type conversions. Explicit type conversions are via T(v) where T is bool, string, int, or float64.

Go and VGo include constants, specified via the const keyword. Constant declarations in VGo must be single-variable with a single initializer; these are the only initializers in the language.

const Pi float64 = 3.14
Go and VGo have only one kind of loop, a for-loop. They look like C for-loops, without the parentheses; the loop body is ALWAYS a compound statement surrounded by curly braces.
var i, sum int
sum = 0
for i = 0; i < 10; i++ {
   sum += i
}
fmt.Println(sum)
Like in C, in Go and VGo, the initialization and post-increment portions of the for-loop are optional. If omitted, the semi-colons may also be omitted. There is no need for a separate "while" loop construct.

sum = 1
for sum < 1000 { /* a while loop */
   sum += sum
   }
The condition is also optional.
   for { /* infinite loop */
   }

Conditionals use syntax similar to loops. Curly braces are required. Unlike loops, the "test" condition is not optional. An else branch is optional.

if x < 0 {
   ...
}
Else branches require curly braces, unless they are (chained) if statements. Due to Go's wimpy semi-colon insertion that is not as good as Unicon's semi-colon insertion rule, chained elses have to be on the line with the curly brace that closes the preceding then-part or else-part.
if x < 0 {
   ...
} else if x < 10 {
   ...
} else {
   ...
}

Go allows a "short statement" prior to the condition. VGo does not

  if v := math.Pow(x, n); v < lim { /* legal Go, not in VGo */
     return v
     }
Go has a switch statement as in C, with implicit break statements after each branch, and with any basic data type allowed. VGo does not do switch statements.
  switch os {
  case "darwin":
  fmt.Println("OS X.")
  case "linux":
  fmt.Println("Linux.")
  default:
  fmt.Println(os, ".")
  }
(Switch statements in Go are not limited to constant labels, making the switch potentially more like a chain of if-statements. A moot observation for readers concerned mainly with VGo.)

Go has a defer statement that specifies a statement (or maybe just a function call) that will execute when the function returns. VGo does not.

Go supports creation of new types via a struct. VGo has structs. Go struct literals can be created in which only some fields are initialized by name. VGo does not support this syntax.

type Vertex struct {
X, Y int
}
...
var v2 Vertex
v2 = Vertex{X: 1}  // Y:0 is implicit
Go has pointers, but no pointer arithmetic. VGo should support just enough pointers to support linked lists. Thus, pointers to structs only.

Go has arrays. VGo has one-dimensional arrays only, including array literals. Go allows declaration of arrays without size; VGo does not. Go has slices -- it uses them aggressively. VGo does not have slices. Go has a variant of the for loop with a range for iterating through an array. VGo does not.

Go has maps. They look like fun. VGo has maps. But VGo does not do map literals.
Go not VGo VGo
var m
m = map[string]Vertex{
     "Bell Labs": Vertex{ 40.68433, -74.39967, },
     "Google": Vertex{37.42202, -122.08408, },
    }
var m map[string]Vertex
m["Bell Labs"] = Vertex{ 40.68433, -74.39967, }
m["Google"] = Vertex{37.42202, -122.08408, }
This includes a delete() function. It is acceptable for a VGo compiler to only allow string and int as index types.

Go has function values and closures. VGo does not.

Go has methods that are functions with a special receiver argument. VGo does not have methods.

Go has interfaces. VGo does not. Go has type assertions. VGo does not. Go has type switches. VGo does not.

When in doubt about VGo features, refer to the Go language Specification. I will add notes below as needed. The easiest way to get out of having to implement something is to ask about it and negotiate.

2. Lexical Rules

The lexical rules of VGo are: the lexical rules of Go. VGo simplifies and reduces the lexical rules of Go a bit.

2.1 Whitespace and Comments

Spaces and tabs separate elements of the source program and are not otherwise considered, except when within a string literal.

VGo comments use // which comments to the end of a line.

C-style comments using /* ... */ should be recognized and result in an error message "C comments not allowed in VGo", along with filename and line number.

A newline in Go may trigger the insertion of a semi-colon, if the final token prior to the newline was an identifier, literal, break, continue, fallthrough, or return, or one of the operators ++, --, ), or }.

2.2 Reserved Words

VGo uses the following reserved words, which are from Go. A vgo compiler must support the following reserved words.

func         map          struct
else         package
const        if           type
for          import       return       var
The rest of the Go reserved words, if they occur should result in a "Go keyword not in VGo" error message, along with the filename and line number, after which the compiler should stop. These are
break        default      interface    select
case         defer        go
chan         goto         switch
fallthrough  range
continue

Caveat: this degree of minimalism may be too extreme. I might relent and add another Go keyword or two, if they seem needed even by toy programs. Also: the Go spec does not refer to built-in type names as keywords, but they would be reserved keywords in most folks' terminology:

bool        string        int          float64

2.3 Operators

vgo uses the following subset of Go operators. The precedence goes from highest at the top, to lowest at the bottom. Associativity is from left to right, except for the assignment operators which are right to left.

OperatorMeaning
()
[]
.
Parenthesis
Subscript
Struct member access
-
!
Unary minus
Unary logical negation
*
/
%
Multiplication
Division
Modulus
+
-
Addition
Subtraction
<
<=
>
>=
less than
less than or equal
greater than
greater than or equal
==
!=
is equal to
is not equal to
&&logical AND
||logical OR
=
+= ++
-= --
assignment. left operand must be a variable
increment
decrement

Other Go operators should result in the error message "Go operator not in VGo", including filename and line number, after which the compiler should stop. Operators that should result in this message include

&     &=    |      |=     
^     *=    ^=     <-
<<    /=    <<=    :=
>>    %=    >>=    :
&^    &^=

2.4 Literals

VGo supports string and numeric literals, and syntax to allow construction of array and map values.

VGo supports ordinary (translated, in Go parlance) String literals enclosed in double quotes, with a basic set of escapes.

Go and VGo integer literals are in familiar decimal, octal or hexadecimal formats, as in 123, 066, or 0x22FF. Floating point constants in Go require an integer followed by either a fractional part, or an exponent part, or both.

Go has imaginary literals and complex constants. These are not in VGo. A VGo compiler should recognize them and emit an appropriate error message.

Go has Runes, which are integer values that identify Unicode code points. VGo is an ASCII subset. A VGo compiler supports the subset of runes that are C/C++-style characters. For other runes, it should emit a lexical error. A VGo compiler supports a subset of backslash escapes, to include \n and \t. Unsupported escapes should result in a lexical error.

2.5 Punctuation

Besides () and [] operators, curly braces are used a lot in VGo. VGo also uses comma as a punctuation token. It might use others.

2.6 Semi-colons

Semi-colons exist but are mostly optional and rare in Go and VGo compared with C/C++. Go and VGo perform semi-colon insertion, with a couple specific lexical rules. For example, the compiler automatically inserts a semicolon at the end of a line if ... the line ended with an identifier, literal, or a closing parenthesis, ++, --, curly brace or square bracket. So we can write

        a = 1
        b = 2
        c = 0
as an equivalent expression for
        a = 1; b = 2; c = 0

Semi-colons are also inserted "for free" before a closing ) or }.

2.7 Identifiers

Variables are identified by names called identifiers. Go allows arbitrary Unicode letters and digits, but VGo follows standard C identifier rules: They must begin with a letter from a through z or A through Z, and then consist of letters and digits. Underscores are allowed in identifiers and treated as letters. The maximum length of a variable name in VGo is 12 and this limit should be enforced.

3. Syntax

The syntax of VGo is based on that of Go.

3.1 Function Syntax

VGo function syntax is a small subset of Go syntax. A function definition consists of a reserved word func followed by an identifier that specifies the function name. These items are followed by a parentheses enclosing parameters, which are a comma-separated list of zero or more variables names and types. After the parameter list, an optional return type may be given. The rest of the function consists of a statement list surrounded by curly braces.

Unlike C/C++ there are no prototypes in Go and VGo. Functions appearing in another file, or later in the same file, can be called (if they are public) using the packagename.Funcname syntax. There is no calling of private functions in another file or later in the same file.

3.2 Control Structures

VGo supports if statements (with an optional else clause), and for loops. Then-parts, else-parts and loop bodies are required to be enclosed by curly brackets by a VGo compiler; the only exception to this restriction on Go syntax is that else-parts that are themselves if statements are allowed without being enclosed in curly brackets.

Note that for-loop syntax is somewhat different in Go/VGo than in C/C++.

3.3 Structures and no OOP

Go does not do classes, but it does methods on structures. Since methods on structures turns out to be an alternate syntax for just declaring functions whose first parameter is a struct, VGo does not bother with methods; programs are expected to write functions whose first parameter are structs.

3.4 Declaration Syntax

Variables must be declared in VGo. Variable declarations like

   var x, y, z int
can occur at two levels: package scope, and local scope. There are no variable initializers.

4. Data Types

4.1 Numbers

VGo has a single size of int and float64 numeric types, which are both 64-bits. A VGo compiler should recognizes others (such as float32 or int32) and emit errors ("Go type not in VGo") with filename and line number.

4.2 Strings

VGo has strings. The translated literals are supported, but not the Go raw string literals in backquotes.

4.3 Arrays

Arrays are sequences, indexed using integers.

var a [32]int

4.4 Maps

Maps are associative arrays, which is to say that their indices are not in sequence. Maps are declared like this:

map[string]int

Maps use built-in functions len() and make() to report their size, or construct a map respectively. Function make() is not a normal function in that its parameter is a compile time type, not a runtime value. For type checking, its return type is always its parameter's type. For code generation, it could generate a function call to a function that takes N parameters and returns a pointer.

4. Built-In Functions

Unlike C/C++, Go and VGo have some built-in functions. In addition, VGo has a small set of predefined packages containing functions.

Summary

Sure, VGo may be a toy language designed for a compiler class. Even with only this much, it may provide a convenient notation for a lot of simple programming tasks such as those faced by students in CS 120.