Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specification tests PR2 #43

Merged
merged 1 commit into from
Nov 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions tests/lang/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,19 @@ Actual error message
## Detailed! explanation for the error cause and possible solutions to it.

block working_example:

# One or more examples of working code

block failing_example:

# Example of the failing code

```

haxscramper marked this conversation as resolved.
Show resolved Hide resolved

## File naming

- `t01_feature_name.nim` - start file name with number to explicitly order features.
- `t01_feature_name_run_fail.nim` - show example of the runtime failure
- `t01_feature_name_comp_fail.nim` - show compilation error related to the feature
- `t01_feature_name_warning.nim` - show compilation warning related to the feature
15 changes: 10 additions & 5 deletions tests/lang/s01_basics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

Here we have tests for the very simple parts of the language, that we often
would not think to test. Mostly this captures minor trivia around:

- comments
- literals
- if statements
- etc
- if, while and case statements
- code block and scopes
- primitive expressions
- Pure-nim user-defined types, without going into details about interfacing
with C and C++ objects, pragmas and any other complications.

Things that we barely think about -- if it fits better somewhere else put it
there. This is a a place for the things too small, rather than too big and fit
nowhere else.
Tests in this section are mostly used to document how most basic interactions
are done - stuff you would find in almost any regular imperative programming
language. Things that are more nim-specific should be placed in `s02_core` or
other sections.
13 changes: 13 additions & 0 deletions tests/lang/s01_basics/s00_atoms/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Atoms

This directory contains specifications for basic language components:

- What is an expression
- What is a statement
- What is "compile time"

For a lot of these, it is difficult to demonstrate without using
concepts from subsequent specification tests; but, it was decided to put them
as a first entry in the specification. When more "advanced" entries are used,
these should be referenced.

60 changes: 60 additions & 0 deletions tests/lang/s01_basics/s00_atoms/t00_builtins.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
discard """
description: '''
This test specifies basic operations used in other tests.
'''
targets:"c cpp js"
"""

block builtin_assert:
## `doAssert` command is used throughout the specification to demonstrate
## the correctness of an expression; ergo, it `doAssert` evaluates the
## expression which must return `true`.

## If the expression is evaluated as true then the assertion is successful
doAssert true

## `0 == 0` is evaluated as true, the assertion is succesful
doAssert 0 == 0

block built_integer_operations:
## Built-in operations on integer values

## Declare integer variable with initial value of `0`
var value: int = 0

## Assert equality of the variable using `==` operator
doAssert value == 0

## Increment value of the variale using `inc` command
inc value

doAssert value == 1

## Decrement value using `dec` command
dec value

## Value of the variable returned to 0
doAssert value == 0

block assert_type_of_the_expression:
## Every expression has a type. This can be checked for using `is` operator
## (or it's reverse `isnot`).

## Declare variable of type `int` and check if expression `value` has this
## type.
var value: int = 0

doAssert value is int
doAssert value == 0

block get_type_of_the_expression_explicitly:
## Using `typeof()` procedure, you can get the type of the expression explicitly.
## Name of the type can be converted to string using `$` operator.
## The `$` operator is an inbuilt operation that converts built-in types to
## string. This will be discussed further in subsequent documents.

var value: int = 0

doAssert value is int
doAssert $int == "int"
doAssert $typeof(value) == "int"
246 changes: 246 additions & 0 deletions tests/lang/s01_basics/s00_atoms/t01_statement.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
discard """
description: '''
This test covers statements, sequential evaluation and control flow.
'''
joinable: false
targets:"c cpp js"
"""
haxscramper marked this conversation as resolved.
Show resolved Hide resolved

## This section of specification explains what *statemetn* is, and two different
## types of statemets - "basic" and "noreturn". Examples of the noreturn statement
## should not be confused with general specification of control-flow alteration that
## is documented in :idx:`s09_control_flow/*.nim`

block sequential_control_flow:
## Multiple statements placed on the same level will be sequentially executed
## unless interrupted by a noreturn statement.

## Statement 1 - declares a variable
var value = 0

## Statement 2 - assign 1 to a variable
value = 1

## Statement 3 - asserts value of the variable is equal to 1
doAssert value == 1

block noreturn_statement:
## noreturn statements enable manipulation of control flow for the program in
## different ways (depending on the context). Each specific type of the control
## flow-altering statement is more thoroughly discussed in their respective
## sections; here we will provide a basic overview of the subject.

block break_statement:
haxscramper marked this conversation as resolved.
Show resolved Hide resolved
## `break` statement can be used inside of loops - (`for`, `while`) and in
## `block` expressions.

block break_unnamed_block:
# Entered `block break_unnamed_block` path
var value = 0
block:
# Entered `block` path
value = 1
break # `break` out of `block` path -> return to previous path of execution.
value = 2
# We now resume `block break_unnamed_block` path of execution

## Only single increment took place - `break` ended the flow of
## the block and resumed its parent flow; therefore, it prevented
## the second expression `value = 2` from being executed.
doAssert value == 1

block break_named_block:
## Unless specified by the `break` operation, it will break only its
## local path of execution. By specifying the name of the `block` you
## wish to break, you can escape several paths of execution at the same
## time.

# enter `break_named_block` path
var value = 0
block name_1:
# enter `name_1` path
value = 1
block name_2:
# enter `name_2` path
value = 2
break name_1 # break out of `name_1` path; since `name_2` is a branch
# of `name_1`, we logically must end its execution too.
value = 3
value = 4

## `break name_1` prevented third and fourth assignment from being executed.
doAssert value == 2

## Should we have instead used `break` or `break name_2` instead of
## `break name_1`, this doAssert would fail; the correct assertion would
## instead be `doAssert value == 4`.

block break_while_loop:
## `break` in a `while` loop immediately stops the cyclic path execution. For more
## details and interactions see tests for `while` statements.
var value = 0
while true:
## Break in the `while` loop terminates it's execution.
break
value = 1

doAssert value == 0

block break_for_loop:
## Break in the `for` loop operates identically to the `while` case - when
## `break` is reached, the loop 'body' or 'path' of execution is stopped. For more details see
## specification on `iterators`.

var value = 0
for i in 0 .. 10:
## Break in the `for` loop terminates it's execution.
break
value = 2

doAssert value == 0

block continue_statement:
## `continue` statement can be used in the `while` and `for` loops to skip
## execution of the current loop. It effectively ends the current cyclic path
## of execution and returns to the beginning of the cyclic path. This is different
## to `break` which ends the cyclic path altogether and instead returns to
## its parent path.

block continue_in_while:
var value = 0
var cond = true

while cond:
## This cyclic path is only entered if `cond` is true.

## First statement in the loop is executed.
cond = false
## Setting this variable to false will mean the next iteration of the
## cyclic path will not proceed since `cond` is no longer true.

## Upon reaching `continue`, control flow is transferred to the next
## iteration.
continue
## The control flow will now return to the beginning of the cyclic path;
## it will re-evaluate `cond` as false and therefore not proceed with
## the next iteration of the cyclic path.


## Assignment to value is never reached.
value = 1

doAssert value == 0

block continue_in_for:
## `continue` in the `for` loop is identical to the `while`
## loop - when reached, it transfers control to the next iteration. For
## more details see specification for the `iteratrors`.
var preContinue = 0
var postContinue = 0

for i in 0 .. 4:
## Statement before `continue` is executed
preContinue = preContinue + i
## Upon reaching `continue`, control flow is returned to the beginning
## of the cyclic path for the next iteration.
continue
postContinue = 9

## Statement placed after continue is never reached
doAssert postContinue == 0

## Statement placed before `continue` is executed on every loop iteration
doAssert preContinue == 0 + 1 + 2 + 3 + 4
## If you flatten out the for loop; this is what you would see:
## ```nim
## i = 0
## preContinue = preContinue + i
## # 1, 2, 3...
## i = 4
## preContinue = preContinue + i
## ```
##
## If we did not have the `continue` operation; we would instead see this:
## ```nim
## i = 0
## preContinue = preContinue + i
## postContinue = 9
## # 1, 2, 3...
## i = 4
## preContinue = preContinue + i
## postContinue = 9
## ```

block return_statement:
## `return` can be used in procedures or macro declaration bodies.
## When reached, it immediately transfers control flow back to the caller of the
## function body (outside the function body). `return` can also be used to
## set resulting value of a procedure or macros - this functionality is
## covered in their respective sections.

var value = 0

value = 1

# Unlike with `block` expressions, we do not immediately enter the `proc`
# path/flow of execution. We first define it, and then enter it when the
# procedure is called. Subsequently, breaking that path of execution would
# return control to where the path was entered, not where it was defined.

proc execReturn() =
value = 2
## When `return` is reached, control flow is immediately transferred out
## of the procedure body
return
value = 3

## We execute the `execReturn` procedure - effectively we have entered the
## path of executions for that procedure. Procedures are looked at in more
## detail in their respective section.
execReturn()
# Path of operations continues when the procedure path reaches its end or
# is `return`ed.

doAssert value == 2

block raise_statement:
## `raise` statement can be used inside of a procedure body or macro to
## create an exception. Concrete details about exceptions, their types and
## handling can be seen in their respective section of the specification.

var value = 0
proc raiseInside() =
value = 1

## Specific type of the exception is not important in this case -
## `ValueError` would not be any different from `OSError` and alike
raise newException(ValueError, "")
value = 2

try:
## When exception is raised it propagates through all function calls (if
## any) until it reaches the topmost level (main module/path from which the first
## function was called) or a `try` statement. For particular details on
## how exceptions affect control flow see respective part of the
## specification. In this case `try` was needed so subsequent `doAssert`
## would be executed correctly.
raiseInside()

except:
# `try`, `except`, `finally`, will be explored in their respective section.
haxscramper marked this conversation as resolved.
Show resolved Hide resolved
discard

doAssert value == 1

block noreturn_annotation:
## Another type of a noreturn statement is a `quit()` procedure - it
## immediatelly terminates whole execution of the program once called.
## If looking at this from a paths or flow perspective, this is effectively
## ending every path of the program.

quit()

## This statement will never be reached because `quit()` was called before.
doAssert false
## If the statement was reached, then the assertion would fail, and the
## test would return a FAIL.
Loading