jeffery@cs.uidaho.edu
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.
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 float64Go, 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.14Go 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
else
s 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 implicitGo 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, } |
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.
VGo
simplifies and reduces the lexical rules of Go a bit.
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
}
.
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 varThe 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
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.
Operator | Meaning |
---|---|
() [] . | 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
& &= | |= ^ *= ^= <- << /= <<= := >> %= >>= : &^ &^=
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.
Besides () and [] operators, curly braces are used a lot in VGo. VGo also uses comma as a punctuation token. It might use others.
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 = 0as an equivalent expression for
a = 1; b = 2; c = 0
Semi-colons are also inserted "for free" before a closing ) or }.
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.
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.
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++.
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.
Variables must be declared in VGo. Variable declarations like
var x, y, z intcan occur at two levels: package scope, and local scope. There are no variable initializers.
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.
VGo has strings. The translated literals are supported, but not the Go raw string literals in backquotes.
Arrays are sequences, indexed using integers.
var a [32]int
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.