Skip to content

Basic Syntax

Socratic_Phoenix edited this page Aug 21, 2017 · 16 revisions

<-- Back | Next -->


Whitespace and Statement Separation

Shnap has an incredibly flexible syntax, which is, in a large part, due to it's treatment of whitespace. Shnap treats all whitespace the same - it makes no distinction between spaces and tabs and newlines. Furthermore, the parser is pretty smart, so most of the time it understands what you're trying to do... but in the rare case that it doesn't, appending a ; to the end of a statement will force the parser to stop reading it. For example, the one case I can think of where this treatment of whitespace is a problem is shown below:

variable = 5
-value.someFunction()

One would expect the above to set variable to 5 and invoke someFunction on -value, but that is not the case. The Shnap parser greedily consumes operators, so it sees the - as a subtraction operator NOT a negation operator, thus resulting in the following parse:

variable = 5 - value.someFunction()

To prevent this, we can simply add a semicolon:

variable = 5;
-value.someFunction()

Though, I'd personally prefer the following:

variable = 5
value.negate().someFunction()

Comments

Shnap has two types of comments, line comments and multi-line comments. A comment may begin with // and continues to the end of the line, or it may begin with /* and continue until the next */. The parser does not recognize comments as tokens - they are preprocessed out, which means the can be inserted between identifiers or in other places other languages wouldn't allow.

Invocation

One core element of Shnap is invoking functions. The syntax for invocation is <expression>(<args>). Since all functions are objects, the invocation succeeds if <expression> returns a function. <args> is a comma separated list of arguments. An argument can be either <expression> or <varName>=<expression>. The first is a required argument, while the second is a default argument. All default arguments must come after all required arguments. Note that the leading ( for parameters must be directly after <expression> (no whitespace). This is to prevent the ambiguous case where ( is being used in an expressions, such as:

something.someFunc()
(1+1).someValue

Variables

Scope

The Shnap scope rules are slightly complex... Every block of code has it's own scope, which has a parent scope (unless it is the top-level scope of a script or module). When a variable is set in a scope, one of two things happens:

  1. If the parent scope, or it's parent, etc. has a variable of the same name, the variable is set in that scope
  2. Otherwise, the variable is set in the current scope

There are some predefined variables in any scope:

  1. this refers to the script or object the scope is within
  2. thisFunc refers to the function the scope is within

When a variable is accessed in a scope, it attempts to get the variable from the current scope, then the parent scope, then it's parent, etc. If a variable could not be found, void is returned.

Variable Pushing

Variable names can start with any number of ^'s, which is the variable pusher. The ^ character explicitly refers the variable to the parent scope of the current scope. That means it allows you to set variables in the parent scope from lower scopes, for example, consider the pseudo-code:

condition = true
if condition {
    ^value = 1
} else {
    ^value = 2
}
println(value)

This code will execute successfully, printing 1.

Variable Localizing

After any (or no) ^'s, a variable name can start with a single :. The : explicitly refers the variable to the current scope. For example:

:val = 2
{
    :val = 3
    {
        :val = 4
        {
            println(:val)
            println(^^^val)
            println(^^val)
            println(^val)
        }
    }
}

Prints:

void
2
3
4

Getting a variable

The syntax for getting a is <varname> or <object>.<varname>.

Setting a variable

The syntax for setting a variable is <object>.<varname> = <value> or <varname> = <value>. The value returned from this operation is <value>. Furthermore, any binary operator can be placed directly before = to transform the expression to <varname> = <varname> <op> <value>. In this case, the value returned is <varname> <op> <value>. Finally, if you are using = assignment (and not operator assignment), you can create a resolvable variable by prepending a ? to the value. A resolvable variable has it's value resolved whenever it is accessed. For example:

val = 0
ref1 = ? val
ref2 = ref1
ref3 = val

for k : range(3) {
    val = k
    println(val)
    println(ref1)
    println(ref2)
    println(ref3)
    println()
}

Prints:

0
0
0
0

1
1
1
0

2
2
2
0

Examples:

fib = importFrom("math", "fib")
math = import("math")
num = -52
val = num + 22
val += num

println(val)
println(math.abs(num))
println(math.abs) //prints the function object associated with math.abs
Clone this wiki locally