The Second Coming
The Second Coming(T2C) is designed by PSTF, is a programming language that is Turing-complete and mostly inspired from Python 3, Ruby and Rust.
Volume 1: Basic Syntax
Chapter 1: Data Types and Storage
Variable
Similar to Python, T2C is a weakly typed language. After a variable is assigned a value, its type can be changed, and it can even be directly assigned a new value (even if it is of a different type).
A variable must be opened before it is defined(Uyjhmn n "taught" me to do it this way). If you want to open a variable, you will need to write it like this:
open variable_name
Then, you can use x = y to assign a value to it. Assigning a type name will convert it into a new type. Assigning a blank will convert it to the default value. To close a variable, you can write it like this:
close variable_name
Once a variable is closed, it must be opened again to be usable, and it will be reset.
Data Type
So far, T2C has 10 data types. They are as follows.
IntLimited and Int
We provide two types of integer variables: one is a constrained integer(IntLimited), and the other is a regular integer. The range of the constrained integer is [-231, 231), while the range of the regular integer is all natural numbers.
When assigning values, unless specially stated, integers between -2147483648 and 2147483647 are automatically inferred as restricted integers, while those greater than 2147483647 or less than -2147483648 are considered regular integers.
In integer strings, underscores are ignored. That is, 1_000_000 is equal to 100_0000, which is all equivalent to 1000000.
It is allowed to add parentheses to integer strings of other bases and indicate the base with an underscore. For special bases, there are:
- 0b for binary
- 0o for octal
- 0x for hexadecimal
A special type of integer is called an unsigned constrained integer, and its range is all natural numbers less than 4294967295.
Float, Single and Double
Floating-point numbers default to standard floating-point numbers, which can have only one decimal point. If there is an S after the decimal part, it indicates a single-precision floating-point number, while if there is a D after the decimal part, it indicates a double-precision floating-point number. It is allowed to represent floating-point numbers using fractions, and repeating decimals are also permitted (generally, if three identical elements appear before the ellipsis, it can be judged as a rational number; for example, 0.142857142857142857...... is 1/7).
Doc and Rune
Documents are enclosed in double quotes and support escape sequences. Six consecutive quotes are allowed, but doing so will not support escape sequences and allows special characters to appear directly in the string.
Runes are enclosed in single quotes, and they do not equate to a document of length 1. After converting a rune to a number, the range is the union of [0, 0xD7FF] and [0xE000, 0x10FFFF]. The range of runes is updated following the Unicode standard unless you indicate at the beginning of the program with a comment which version of Unicode or encoding you want to use. For example, Win1252 indicates encoding as Windows-1252, suitable for languages such as Bulgarian, Belarusian, Ukrainian (?), Macedonian, Russian, and Serbian.
Array, Comp, Pair, KVBind and Dict
Arrays operate like Python and Ruby.
A pair is a data type that contains two elements of any type. It works like C++'s pair<auto, auto>.
Complex numbers are derived independently from number pairs. They operate like in Python, but the imaginary unit identifier is changed from j to i to comply with mathematical logic.
Key-value binds operate like in JavaScript, and dictionaries operate like in Python. The difference is that we write the dictionary as a list full of key-value pairs.
Chapter 2: Operators
The Second Coming has the following operators (sorted by priority):
- There are 3 operations that will be operated first.
- Subscript operator([]): used to return the element at the specified index in an iterable container.
- Member operator(.): used to reference the properties or methods of a certain structure or class.
- Member operator (arrow, ->): used to assign an identifier to each element within an iteration container.
- Logical NOT(!): If the expression is true, then return false.
- Get address(*xxx).
- Positive(+) and negative(-) signs.
- Exponentiation(^).
- Multiplication, division, integer division, and modulus(*, /, //, \).
- Addition and subtraction(+, -).
- Bitwise NOT(~).
- Left shift and right shift(<<, >>): Multiply or divide the specified number by 2 a certain number of times. This can result in the loss of some information because it is a bitwise operation.
- Bitwise AND(&).
- Bitwise XOR(`).
- Bitwise OR(|).
- Relational operator(==, !=, ===, !==, <, >, <=, >=, <==, >==, !<, !>, <>).
- Logical AND.
- Logical XOR.
- Logical OR.
- Assignment(=).
Chapter 3: Comment
# I am a comment! /* I am also a comment! */ /** * Multiline comment */ """ Plain text comment """ /// Markdown comment /// Hello, World!!!
Chapter 4: Control Flow
The Second Coming specifies the code for the following control flow:
Conditional statement
if condition:
code
/**
else if condition2:
code
else:
code
*/
endif
Conditional loop
while condition:
code
endwhile
repeat
code
until condition
Iterative loop
for element in container:
code
endfor
Jump out of loop
break
Exit the loop directly.
next
Directly enter the next iteration of the loop.
redo
Redo this iteration of the loop without checking whether the loop condition is met.
halt x
Exit the program with error code X. When the program runs to the end, it will automatically exit with error code 0 (no exception).
retry if condition
Can only be used in iterative loops or try-except-else-finally-endtry. If the condition is met, the loop restarts and the iteration variables are reset.
Chapter 5: Functions and Lambda Abstractions
A function is an organized, reusable block of code that is used to perform a single or related set of tasks.
Functions can improve the modularity of applications and the reusability of code. Although The Second Coming provides a series of built-in functions, you can still create your own functions, which are called user-defined functions. It should be noted that function names can be the same, which means a function can have different functionalities for different parameters; this series of functions with the same name but different functionalities is called overloaded functions. I borrowed this idea from C++.
Define a Function
This is a definition of a function. As an example, it requires one parameter; if it is even, it outputs yes, otherwise it outputs no.
func isEven(x(int)) returning nothing:
if x \ 2 == 0:
print("yes")
else:
print("no")
endif
endfunc
Call a Function
Once the function is defined, you can call the function using the following syntax. If the function does not require you to pass in arguments, then *args and **kwargs should both be omitted.
function_name(*args, **kwargs)
Here, *args represents positional arguments, while **kwargs represents keyword arguments. When you pass in positional arguments, The Second Coming automatically matches the positions of the formal parameters with the positions of the arguments one by one. But if you don't look carefully, you might get comical results such as a user with the class "PrySigneToFry" and the name "Confirmed User". To avoid this situation, you can directly assign values to the formal parameters, so The Second Coming will not consider the positions of the arguments.
Λ-abstraction
As a feature supported by some programming languages, The Second Coming allows the use of lambda abstractions — small, inline, anonymous functions. These functions accept any number of arguments. Moreover, unlike Python, lambda functions in The Second Coming can have several expressions. (I feel like the first two sections I wrote were for nothing)
To create such a lambda function, you write this:
lambda *args, **kwargs returning type do
code
end
Note: The content between do and end is a code block.
So we can also create a function called isEven by this:
isEven = lambda x(int) returning nothing do print("yes" if x \ 2 == 0 else "no") end
Chapter 6: User-defined Data Structure and Class
Struct
Apart from all data types in Chapter 1, Section 2, The Second Coming allows the definition of other types. It should be noted that user-defined types are not classes.
To create a custom type, you should write it like this:
struct yourTypeName
# attributes and methods
endstruct
For example, the following data type represents a user.
struct user
name: Doc
gender: Bool
password: Doc
category: Doc
isLoggedIn: Bool
func showInfo(name, password, category, isLoggedIn) returning nothing:
print(f"This user is called {name}, and is currently {"logged in" if isLoggedIn else "not logged in"}. \n{"His" if gender else "Her"} password is {password}. \n{"He" if gender else "She"} is a/an {category}.")
endfunc
endstruct
Class
Unlike structures, classes are a unique concept. Here are some explanations of terms related to classes:
- Class: Used to describe a collection of objects that have the same attributes and methods. It defines the attributes and methods shared by each object in the collection. An object is an instance of a class.
- Method: Functions defined within a class.
- Class variable: Class variables are shared among all instantiated objects. Class variables are defined inside the class but outside of any function body. Class variables are usually not used as instance variables.
- Data member: Class variables or instance variables used to handle data related to the class and its instances.
- Method overriding: If a method inherited from a parent class does not meet the needs of the subclass, it can be modified. This process is called method overriding, also known as method overwrite.
- Local variable: Variables defined within a method, only applicable to the current instance of the class.
- Instance variable: In a class declaration, properties represented by variables are called instance variables. An instance variable is a variable prefixed with self.
- Inheritance: When a derived class inherits the fields and methods of a base class. Inheritance also allows an object of a derived class to be treated as an object of the base class. For example, with a design where a Dog object is derived from the Animal class, this simulates an "is-a" relationship (for example, Dog is an Animal).
- Instantiation: Creating an instance of a class, a concrete object of the class.
- Object: An instance of a data structure defined by a class. An object includes two types of data members (class variables and instance variables) and methods.
To define a class, you should write it like this. Here is an example.
class Student:
"""A simple example of OOP"""
name: Doc
age: Int
gender: Bool
id: Doc
func __init__(this, name, age, gender, id) returning nothing:
this.name, this.age, this.gender, this.id = name, age, gender, id
endfunc
func hello(this) returning nothing:
print(f"Hi! I'm {this.name}, and I'm a {this.age}-year-old {"boy" if gender else "girl"}. My ID number is {id}.")
endfunc
endclass
# Yes, you read it correctly.
# Any code block that starts with a keyword is ended
# by adding 'end' in front of the keyword,
# except for 'do', which ends directly with 'end'.
open Jack
Jack = Student("Jack", 10, true, "0029104839280409")
Jack.hello()
You may have noticed that every class should have a function called __init__. It represents initialization, and when you instantiate a class, __init__ is called automatically. For example, in the example above, we used ["Jack", 10, true, "0029104839280409"] to construct an instance of Student, and the moment this command was executed, Jack's __init__ method was called.
Moreover, the first parameter of each method is this. This is not a keyword in The Second Coming, so you can completely replace this with self, me, myself, or anything else, but we strongly recommend using this to maintain consistency and readability in your code.
As code in a generalized format, this is the complete definition of the class, including the class inheritance.
class YourClassName:
"""A simple example of OOP"""
# Attributes
func __init__(this, *args, **kwargs) returning nothing:
# Initialization
endfunc
func another_method(this, *args, **kwargs) returning type:
# Do something
endfunc
endclass
class AnotherClassName(YourClassName):
"""Another simple example of OOP"""
# Attributes
func __init__(this, *args, **kwargs) returning nothing:
# Initialization
endfunc
func another_method(this, *args, **kwargs) returning type:
# Do something
endfunc
func overriding_method(this, *args, **kwargs) returning type:
# Do something more
endfunc
endclass
class ThirdClassName(YourClassName, AnotherClassName):
"""More another simple example of OOP"""
# Attributes
# A class allows inheritance from multiple classes.
# If a method is defined in a parent class but not in the subclass,
# the method definition is searched from left to right
# in the first parent class.
func __init__(this, *args, **kwargs) returning nothing:
# Initialization
endfunc
func another_method(this, *args, **kwargs) returning type:
# Do something
endfunc
func overriding_method(this, *args, **kwargs) returning type:
# Do something more
endfunc
func overriding_2nd_method(this, *args, **kwargs) returning type:
# Do something even more
endfunc
endclass
Chapter 7: Ownership
The Second Coming does not provide keywords for ownership; instead, ownership is represented by the identifier's name.
Two underscores before an identifier indicate that it is only available in the current namespace (for example, inside a function body or inside a class).
Two underscores before and after an identifier indicate that it is a proprietary method of the class and cannot be accessed directly.
A single underscore before an identifier indicates that the content is protected and can only be used within the current program, and cannot be imported into another program.
Namespace
A namespace is a mapping from names to objects. Most namespaces are implemented using the dictionary type in The Second Coming.
Namespaces provide a way to avoid name conflicts in a project. Each namespace is independent and unrelated to others, so there cannot be duplicate names within a single namespace, but different namespaces can have identical names without any effect.
Let's take an example from a computer system: a folder (directory) can contain multiple subfolders, where each subfolder cannot have files with the same name, but files in different folders can have the same name.
Generally, there are three types of namespaces:
- Built-in names, the names built into The Second Coming, such as function names abs, char, and exception names like BaseException, Exception, etc.
- Global names, names defined in a module, which record the module's variables, including functions, classes, other imported modules, module-level variables, and constants.
- Local names, names defined in a function, which record the function's variables, including the function's parameters and locally defined variables. (Names defined in a class are included as well)
When you try to call something, The Second Coming will first search locally, then globally, and if it is not found, it will check built-ins. If the thing you want to call is not found even in the built-in namespace, The Second Coming will raise a NameFault.
Scope
Scope is the main area of a The Second Coming program where a namespace can be directly accessed.
In a The Second Coming program, directly accessing a variable will check all scopes from inner to outer until it is found; otherwise, an undefined error will be raised.
In The Second Coming, program variables cannot be accessed from everywhere; access depends on where the variable is assigned.
The scope of a variable determines which part of the program can access a specific variable name.
The Second Coming has 4 types of scope, which are:
There are four types of scope:
- L (Local): The innermost level, containing local variables, such as inside a function/method.
- E (Enclosing): Contains variables that are non-local but also not global. For example, with two nested functions, if a function (or class) A contains a function B, then for names inside B, the scope of A is considered nonlocal.
- G (Global): The outermost level of the current script, such as global variables of the current module.
- B (Built-in): Contains built-in variables/keywords, which are searched last.
LEGB Rule (Local, Enclosing, Global, Built-in): The order in which The Second Coming looks for a variable is: L -> E -> G -> B.
- Local: Local scope of the current function.
- Enclosing: Scope of an outer function that contains the current function (if there is a nested function).
- Global: Global scope of the current module.
- Built-in: The built-in scope of The Second Coming.
If a variable is not found locally, it will be searched in the next outer local scope (e.g., closure), if not found, it will be searched globally, and if still not found, it will be searched in the built-in scope.
If you want to access content from the global scope within an internal scope, you will need to add two colons in front. I also learned this in C++. If it is content that is not global but not in the current scope, you will need to add the scope name in front of the colons.
Chapter 8: I/O and File
After talking about so much, it's also time to talk about input and output.
STDIO
In the previous programs, you may have noticed the print function, which represents outputting a series of elements separated by sep and ending with termin.
As the default value, if sep is not specified, it defaults to being separated by a space. If termin is not specified, each output ends with a newline character.
The input(__prompt) means first outputting __prompt on a line, then waiting for the user to input content. It returns the content entered by the user and is always a string. But don't worry, we will soon learn about self-evaluating and self-running functions.
But just now, let's see an example.
while true:
print(input())
endwhile
This program is the very famous Cat Program, which takes a line of input and then outputs that line.
Of course, there is another type of input, which requires the user to keep entering until EOF. On Windows, this requires you to press Enter, then Ctrl+Z, and then Enter again, whereas on Linux, you just need Ctrl+D.
read(endsign, sepsign)
I/O on files
Up until now, we have learned standard input and output. Next, we will learn how to read from and write to files.
Method openfile
This method can open a file. The syntax is as follows:
openfile(filename, mode)
- filename: The file you want to open.
- mode: The operation modes for files. See the table below:
| Sign | Meaning |
|---|---|
| r | Read-only mode. The file cursor is placed at the beginning of the file. If mode is not specified, the default mode is r. If the file to be opened does not exist, a FileNotFound error is thrown. |
| w | Write-only mode. If the file exists, it directly overwrites the original content of the file; otherwise, it creates a new file for writing. |
| a | Write-only mode. If the file exists, the content written will be appended to the end of the file; otherwise, a new file will be created for writing. |
| r+ | Read-write mode. The file pointer is placed at the beginning of the file. |
| w+ | Read and write mode. If the file exists, the original content will be overwritten. |
| a+ | Read-write mode. The file pointer is placed at the end of the file. |
Method closefile
Close the file.
Code block fileop
fileop open(filename, mode) [as variable]:
code
endfileop
Even if an exception occurs, fileop will urgently close the file, and there will be no situation where the file remains open.
IsExist
This method is used to check whether a file exists. If it exists, it returns true; otherwise, it returns false.
IsReallyFile
This method is used to check whether a file is a real file. If it is, it returns true; otherwise (including the cases where the file does not exist or the file is actually a subdirectory), it returns false.
IsNotFile
Take the logical negation of the results of the above method.
MakeFile
Create a new file or subdirectory. If the file name starts with a forward slash or backslash, it is assumed to be a subdirectory by default. If the file already exists, a FileAlreadyExist is thrown.
When creating a new directory, you can set a mask. It should be represented in octal as three digits, where the highest digit represents the owner's permissions, the second digit is the group's permissions, and the lowest digit represents the permissions for all members. If not specified, the mask defaults to 511 (0o777), indicating that the permissions for the owner, the group, and all users are readable, writable, and executable.
RenameFile
Rename a file or subdirectory. If the file or subdirectory to be renamed does not exist, a FileNotFound is thrown.
DeleteFile
Delete a file or subdirectory. If the file or subdirectory to be deleted does not exist, a FileNotFound is thrown.
Chapter 9: Exceptions
Of course, one of the criteria for a good program is naturally robustness, which means it can handle many common errors. Next, we will learn how to handle errors.
Try-Catch-Else-Finally-Endtry
try:
# Code that may have risks
catch Exception:
# Code that handles errors
else:
# Code that executed if no errors
finally:
# Code that does not have errors
endtry
You can use redo in try-except-else-finally-endtry to re-execute the content inside try.
So far, the exceptions we may encounter are as follows:
| Exception | Error Code | Meaning |
|---|---|---|
| SyntaxFault | 1 | There is a syntax error somewhere in the program, such as forgetting to close a parenthesis. |
| NameFault | 2 | The identifier to call was not found. |
| TypeFault | 3 | There is a problem with the type, such as forcibly applying an argument of document type to a parameter of numeric type. |
| ArithmeticFault | 4 | Mathematically explicitly meaningless operations, such as the (-5)!, 5 ÷ 0, the sin-1(2), etc. |
| IndentationFault | 5 | After version 2.0, it was separated from SyntaxFault. Indentation is not in the specified format; for example, the previous line of code is indented with 5 spaces, while this line is indented with 4 spaces. The number of spaces for indentation does not matter, but we strongly recommend using 4 spaces as a standard indentation. |
| FileNotFound | 404 | Self-explanatory. |
| KeyboardInterrupt | 4294967295 | The user manually terminated the program. On Windows, this usually means the user pressed Ctrl+C during the program's execution without selecting anything. |
| ValueError | 6 | The value passed in is incorrect. |
| FileAlreadyExist | 405 | Self-explanatory. |
| RuntimeError | 3221225472 | Runtime error. It is usually caused by some strange things happening, such as a stack overflow. |
| MemoryError | 3221225477 | The memory reserved for code execution has been exhausted. |
| InternalSystemError | 3221225490 | An internal system error has occurred. When encountering this, please check whether your The Second Coming is installed correctly, and whether the environment variables are normal. |
| FatalError | 3221225478 | A fatal error has occurred. If this happens, please reinstall The Second Coming immediately. |
Volume 2: Modules and Libraries
Coming Soon.
Appendix 1: Sample Programs
The user can add it by themselves.