Slag:Control Structures

From Plasmaworks

Jump to: navigation, search

Contents

Single Line vs Multi-Line Commands

  • if, while, and forEach support single line commands as well as multi-line commands.
  • If a control structure has commands on the same line after the condition, it is taken to be a single line statement - all commands on that line (separated by semi-colons) are part of the body and no terminal end marker is required.
  • If a control structure doesn't have commands immediately following the condition, it's considered a multi-line command and an appropriate "end" command is looked for.
  • A multi-part if/elseIf/else cannot mix and match single and multi-line blocks. They must all be single or all be multi (otherwise nested if statements could be ambiguous).
  • Single line statements may not have any nested control structures.


If

Syntax:

 if (condition)
   ...
 elseIf (condition)
   ...
 else
   ...
 endIf
 if (condition) single; line; commands
 elseIf (condition) single; line; commands
 else single; line; commands


Example:

 if (x > 0) println( "$ is positive" (x) )
 elseIf (x < 0) println( "$ is negative" (x) )
 else println( "0 is zero" )
  • Like all conditionals in Slag, if requires parentheses around the condition.
  • escapeIf skips to the command after the enclosing endIf.


Which and WhichIs

Example:

 which (ch)
   case  9: return "TAB"
   case 10, 13: return "EOL"  # well, kinda...
   case 27: return "ESC"
   case 32..126: return ""+Char(ch)
   others: return "^" + Char(ch+64)
 endWhich
 whichIs (day)
   case Day.saturday, Day.sunday:
     println( "weekend" )
 
   others:
     println( "weekday" )
 endWhichIs
  • The which structure replaces the switch or select of other languages.
  • A case may be any expression and is not limited to being a constant.
  • Internally which statements are converted into if statements. The expression is saved in a local variable and then compared to each case value.
  • which cases resolve to "==" comparisons while whichIs cases resolve to "is" comparisons.
  • No break commands are necessary.
  • others: replaces default:
  • Independent values can be part of the same case when separated by commas ("case a,b,c:" ).
  • A case may contain a simple range of values, e.g. "case 1..10:". This is converted into a two-part conditional ("if (temp_value >= 1 and temp_value <= 10)").
  • escapeWhich skips to the command after the enclosing endWhich and likewise for escapeWhichIs.


Contingent

Syntax:

 contingent
   # body (regular commands)
   ...
   necessary (condition)
   ...
   sufficient (condition)
   ...
 satisfied
   # commands if execution reaches end of body or any 'sufficient' cmd true 
 unsatisfied
   # commands if any 'necessary' tests false.
 endContingent

Example:

 method print_primes( Int32 low, Int32 high ):
   forEach (n in low..high)
     contingent
       necessary (n >= 2)
       sufficient (n == 2)
       forEach (d in {2,3..sqrt(n) step 2})
         necessary (n % d != 0)
       endForEach
     satisfied
       println( "$ is a prime number" (n) )
     endContingent
   endForEach
  • Slag introduces contingents as a new kind of conditional. They are similar in flow to a try/catch block but they deal with positive and negative logic tests rather than error generation.
  • They are useful when it will take several consecutive commands to determine whether to execute "success" or "failure" code.
  • Any commands may be placed in the body. These commands can include necessary(...) and sufficient(...) logic tests.
  • If a necessary condition is true, execution proceeds to the next command. If false, execution jumps to the unsatisfied clause.
  • If a sufficient condition is false, execution proceeds. If true, execution jumps to the satisfied clause.
  • If all the body code is executed with no necessary conditions being false and no sufficient conditions being true, execution proceeds with the satisfied clause.
  • If the satisfied clause executes, the unsatisfied clause is skipped.
  • The satisfied and unsatisfied clauses are both optional. If one or the other is not present the contingent behaves as if it were empty.
  • escapeContingent skips to the command after the enclosing endContingent.


While

Syntax:

 while (condition)
   ...
 endWhile
 while (condition) single; line; commands

Example:

 # Sum together the digits of a non-negative integer until one digit is left.
 while (n > 0)
   n = n/10 + n%10
 endWhile
  • escapeWhile skips to the command after the enclosing endWhile.
  • nextIteration skips the remainder of the current iteration and proceeds onto the next iteration.


Loop

Syntax:

 loop
   ...
 endLoop

Example:

 # Keep reading in numbers from some reader until a zero is read.
 loop
   local var n = reader.read
   if (n == 0) escapeLoop
   list.add(n)
 endLoop
  • loop loops indefinitely and is a convenient alternative to "while (true) ...".
  • escapeLoop skips to the command after the enclosing endLoop.
  • nextIteration skips the remainder of the current iteration and proceeds onto the next iteration.


ForEach

Syntax:

 # multi-line variants
 forEach (range)
   ...
 endForEach
 forEach (var_name in range)
   ...
 endForEach
 forEach (var_name of range)
   ...
 endForEach
 # single line variants
 forEach (range) single; line; commands
 forEach (var_name in range) single; line; commands
 forEach (var_name of range) single; line; commands

Examples:

 forEach (1..100) println("I will use iteration to make my life easier.")
 forEach (line in LineReader(File("settings.txt"))) println(line)
 forEach (ch in "nilbog".reverse_order) print(ch)
 println
 forEach (entity in mobs)
   entity.update
   if (entity.is_dead) removeCurrent entity
 endForEach
 method index_of( Real64 n ).Int32:
   forEach (index of data)
     if (data[index] == n) return index
   endForEach
   return -1
  • escapeForEach skips to the command after the enclosing endForEach.
  • nextIteration skips the remainder of the current iteration and proceeds onto the next iteration.
  • Read the notes on advanced forEach usage to learn about:
    • How forEach loops are processed internally.
    • How to write classes that are compatible with forEach.
    • What causes concurrent modification errors and how to avoid them.
    • How the removeCurrent command works.


Try/Catch & Throw

Syntax:

 try
   ...
 catch (MostSpecificType err)
   ...
 catch (LessSpecificType err)
   ...
 catch (MostGeneralType err)
   ...
 endTry
 throw Error("badwrong")

Example:

 try
   load_save_data( File("save.txt") )
 catch (FileError err)
   set_up_default_data()
 endTry
  • The throw command takes an Exception (or subclass) object and unwinds the call stack to the most recently-defined catch of the correct type, no matter many nested calls you're currently in.
  • Class Error extends class Exception and all built-in exceptions are extended from type Error.
  • There are no checked exceptions in Slag - errors may be thrown at any time (without writing a declaration in the method signature) and enclosing try/catch blocks are entirely optional.
  • Exception objects that are instanceOf the first catch type are handled by that catch and the other catches are skipped.
  • Slag does not have a finally clause.
Personal tools