- Language Overview
- Lexical Structure
- Data Types
- Operators
- Control Flow
- Built-in Methods
- Static Methods
- Error Handling
- Grammar Specification
- Implementation Details
Simple Programming Language (SPL) is a dynamically-typed, interpreted programming language designed for educational purposes and rapid prototyping. It features:
- Dynamic Typing: Variables can hold values of any type
- Object-Oriented Methods: Built-in methods for all data types
- Method Chaining: Support for fluent interfaces
- Static Methods: Class-level utility functions
- Memory Management: Automatic garbage collection via JavaScript runtime
- Browser-Based: Runs entirely in web browsers using Pyodide
- Simplicity: Easy to learn syntax similar to Python and JavaScript
- Consistency: Uniform method naming and behavior across types
- Expressiveness: Support for complex operations through method chaining
- Safety: Runtime type checking and comprehensive error reporting
SPL recognizes the following token types:
if, else, while, for, in, break, True, False, and, or, not, range
Arithmetic: +, -, *, /, %
Comparison: ==, !=, <, <=, >, >=
Assignment: =
Logical: and, or, not
Parentheses: ( )
Brackets: [ ]
Braces: { }
Semicolon: ;
Comma: ,
Dot: .
- Numbers: Integer and floating-point literals
- Examples:
42,3.14,-17,0.5
- Examples:
- Strings: Double-quoted string literals
- Examples:
"hello","world",""
- Examples:
- Booleans:
True,False
- Must start with a letter or underscore
- Can contain letters, digits, and underscores
- Case-sensitive
- Examples:
name,_private,counter1
- Single-line comments start with
# - Example:
# This is a comment
- Whitespace (spaces, tabs) is ignored except for indentation context
- Statements are terminated by semicolons (
;) - Newlines are generally ignored unless they separate statements
Represents both integers and floating-point numbers.
Internal Representation: JavaScript Number (64-bit floating point)
Literal Syntax:
42 # Integer
3.14 # Float
-17 # Negative integer
0.5 # Decimal
Methods:
abs()→ Number: Absolute valuesign()→ Number: Sign (-1, 0, 1)round(digits?)→ Number: Round to specified decimal placesfloor()→ Number: Round down to nearest integerceil()→ Number: Round up to nearest integersqrt()→ Number: Square rootpow(exponent)→ Number: Raise to powertostring()→ String: Convert to string representation
Represents sequences of Unicode characters.
Internal Representation: JavaScript String (UTF-16)
Literal Syntax:
"hello" # Basic string
"world!" # String with punctuation
"" # Empty string
Methods:
length()→ Number: String lengthupper()→ String: Convert to uppercaselower()→ String: Convert to lowercasestrip()→ String: Remove leading/trailing whitespacestartswith(prefix)→ Boolean: Check if starts with prefixendswith(suffix)→ Boolean: Check if ends with suffixcontains(substring)→ Boolean: Check if contains substringfind(substring)→ Number: Find index of substring (-1 if not found)replace(old, new)→ String: Replace all occurrencessplit(separator?)→ List: Split into list of stringsslice(start, end?)→ String: Extract substring
Represents logical truth values.
Internal Representation: JavaScript Boolean
Literal Syntax:
True # Boolean true
False # Boolean false
Methods:
tostring()→ String: Convert to string ("true" or "false")tonumber()→ Number: Convert to number (1.0 or 0.0)not()→ Boolean: Logical negation
Represents ordered collections of values.
Internal Representation: JavaScript Array
Literal Syntax:
[1, 2, 3] # List of numbers
["a", "b", "c"] # List of strings
[1, "hello", True] # Mixed-type list
[] # Empty list
Indexing Syntax:
list[0] # First element
list[1] # Second element
Methods:
length()→ Number: List lengthcontains(value)→ Boolean: Check if contains valueindex(value)→ Number: Find index of value (-1 if not found)append(value)→ None: Add value to endprepend(value)→ None: Add value to beginningpop()→ Any: Remove and return last elementremove(value)→ None: Remove first occurrence of valuesort()→ None: Sort list in-placereverse()→ None: Reverse list in-placeslice(start, end?)→ List: Extract sublistjoin(separator?)→ String: Join elements into stringcopy()→ List: Create shallow copyclear()→ None: Remove all elements
| Operator | Description | Example | Result |
|---|---|---|---|
+ |
Addition | 5 + 3 |
8 |
- |
Subtraction | 5 - 3 |
2 |
* |
Multiplication | 5 * 3 |
15 |
/ |
Division | 6 / 3 |
2.0 |
% |
Modulo | 7 % 3 |
1 |
| Operator | Description | Example | Result |
|---|---|---|---|
== |
Equal | 5 == 5 |
True |
!= |
Not equal | 5 != 3 |
True |
< |
Less than | 3 < 5 |
True |
<= |
Less than or equal | 5 <= 5 |
True |
> |
Greater than | 5 > 3 |
True |
>= |
Greater than or equal | 5 >= 3 |
True |
| Operator | Description | Example | Result |
|---|---|---|---|
and |
Logical AND | True and False |
False |
or |
Logical OR | True or False |
True |
not |
Logical NOT | not True |
False |
From highest to lowest precedence:
- Method calls (
.) - Unary operators (
not,-) - Multiplicative (
*,/,%) - Additive (
+,-) - Comparison (
<,<=,>,>=) - Equality (
==,!=) - Logical AND (
and) - Logical OR (
or) - Assignment (
=)
if condition {
# statements
} else {
# statements
}
Syntax Rules:
- Condition must evaluate to a boolean
- Braces
{}are required elseclause is optional
while condition {
# statements
break; # optional early exit
}
Syntax Rules:
- Condition must evaluate to a boolean
breakstatement exits the loop immediately- Infinite loops are possible if condition never becomes false
# Iterate over range
for variable in range(start, end, step) {
# statements
}
# Iterate over list
for variable in list {
# statements
}
Syntax Rules:
- Variable is automatically declared in loop scope
range(n)creates sequence 0 to n-1range(start, end)creates sequence start to end-1range(start, end, step)creates sequence with custom stepbreakstatement exits the loop immediately
length()→ Number- Returns the number of characters in the string
- Example:
"hello".length()→5
-
upper()→ String- Converts all characters to uppercase
- Example:
"hello".upper()→"HELLO"
-
lower()→ String- Converts all characters to lowercase
- Example:
"HELLO".lower()→"hello"
-
strip()→ String- Removes leading and trailing whitespace
- Example:
" hello ".strip()→"hello"
-
startswith(prefix)→ Boolean- Checks if string starts with the given prefix
- Example:
"hello".startswith("he")→True
-
endswith(suffix)→ Boolean- Checks if string ends with the given suffix
- Example:
"hello".endswith("lo")→True
-
contains(substring)→ Boolean- Checks if string contains the given substring
- Example:
"hello".contains("ell")→True
-
find(substring)→ Number- Returns index of first occurrence of substring, -1 if not found
- Example:
"hello".find("ell")→1
-
replace(old, new)→ String- Replaces all occurrences of old substring with new substring
- Example:
"hello world".replace("world", "SPL")→"hello SPL"
-
split(separator?)→ List- Splits string into list using separator (default: single space)
- Example:
"a,b,c".split(",")→["a", "b", "c"]
-
slice(start, end?)→ String- Extracts substring from start index to end index (exclusive)
- Example:
"hello".slice(1, 3)→"el"
-
abs()→ Number- Returns absolute value
- Example:
(-5).abs()→5
-
sign()→ Number- Returns -1 for negative, 0 for zero, 1 for positive
- Example:
(-5).sign()→-1
-
round(digits?)→ Number- Rounds to specified decimal places (default: 0)
- Example:
3.14159.round(2)→3.14
-
floor()→ Number- Rounds down to nearest integer
- Example:
3.7.floor()→3
-
ceil()→ Number- Rounds up to nearest integer
- Example:
3.2.ceil()→4
-
sqrt()→ Number- Returns square root
- Example:
16.sqrt()→4
-
pow(exponent)→ Number- Raises number to given power
- Example:
2.pow(3)→8
tostring()→ String- Converts number to string representation
- Example:
42.tostring()→"42"
-
tostring()→ String- Converts boolean to string ("true" or "false")
- Example:
True.tostring()→"true"
-
tonumber()→ Number- Converts boolean to number (1.0 or 0.0)
- Example:
True.tonumber()→1.0
-
not()→ Boolean- Returns logical negation
- Example:
True.not()→False
-
length()→ Number- Returns number of elements in list
- Example:
[1, 2, 3].length()→3
-
contains(value)→ Boolean- Checks if list contains the given value
- Example:
[1, 2, 3].contains(2)→True
-
index(value)→ Number- Returns index of first occurrence of value, -1 if not found
- Example:
[1, 2, 3].index(2)→1
-
append(value)→ None- Adds value to end of list
- Example:
list.append(4)modifies list in-place
-
prepend(value)→ None- Adds value to beginning of list
- Example:
list.prepend(0)modifies list in-place
-
pop()→ Any- Removes and returns last element
- Example:
[1, 2, 3].pop()→3, list becomes[1, 2]
-
remove(value)→ None- Removes first occurrence of value
- Example:
list.remove(2)modifies list in-place
-
sort()→ None- Sorts list in ascending order (in-place)
- Example:
[3, 1, 2].sort()→ list becomes[1, 2, 3]
-
reverse()→ None- Reverses list order (in-place)
- Example:
[1, 2, 3].reverse()→ list becomes[3, 2, 1]
-
slice(start, end?)→ List- Returns new list with elements from start to end (exclusive)
- Example:
[1, 2, 3, 4].slice(1, 3)→[2, 3]
-
join(separator?)→ String- Joins elements into string with separator (default: no separator)
- Example:
[1, 2, 3].join(", ")→"1, 2, 3"
-
copy()→ List- Creates shallow copy of list
- Example:
original.copy()→ new list with same elements
-
clear()→ None- Removes all elements from list
- Example:
list.clear()→ list becomes[]
Static methods are called on class names rather than instances.
Math.pi()→ Number: Mathematical constant π (3.141592653589793)Math.e()→ Number: Mathematical constant e (2.718281828459045)
Math.random()→ Number: Random float between 0 and 1
Math.max(list)→ Number: Maximum value in listMath.min(list)→ Number: Minimum value in listMath.sum(list)→ Number: Sum of all values in listMath.average(list)→ Number: Average of all values in list
Math.sin(radians)→ Number: Sine functionMath.cos(radians)→ Number: Cosine functionMath.tan(radians)→ Number: Tangent functionMath.log(number)→ Number: Natural logarithm
String.fromcode(charCode)→ String: Create string from ASCII/Unicode codeString.repeat(string, count)→ String: Repeat string n timesString.join(list, separator?)→ String: Join list elements into string
String.ascii_letters()→ String: All ASCII letters (a-z, A-Z)String.digits()→ String: All digit characters (0-9)
List.empty()→ List: Create empty listList.fill(count, value)→ List: Create list with repeated valueList.range(start, end?, step?)→ List: Create list from rangeList.from_string(string)→ List: Convert string to list of characters
SPL provides comprehensive error reporting for various types of runtime errors.
- Invalid Method Call: Calling non-existent method on a type
- Invalid Argument Type: Passing wrong type to method
- Invalid Operation: Performing unsupported operation
- Division by Zero: Dividing number by zero
- Index Out of Bounds: Accessing list element beyond bounds
- Variable Not Defined: Using undefined variable
- Invalid Range: Using invalid range parameters
- Unexpected Token: Invalid token in source code
- Missing Delimiter: Missing semicolon, brace, etc.
- Invalid Expression: Malformed expression
Error messages include:
- Error Type: Clear classification of error
- Location: Line number where error occurred
- Description: Human-readable explanation
- Context: Relevant code snippet when possible
- Errors halt execution immediately
- Variable state is preserved for debugging
- Error output is displayed in IDE console
- Execution can be restarted after fixing errors
program = statement*
statement = assignment_statement
| expression_statement
| if_statement
| while_statement
| for_statement
| break_statement
assignment_statement = identifier "=" expression ";"
expression_statement = expression ";"
if_statement = "if" expression block ("else" block)?
while_statement = "while" expression block
for_statement = "for" identifier "in" expression block
break_statement = "break" ";"
block = "{" statement* "}"
expression = logical_or
logical_or = logical_and ("or" logical_and)*
logical_and = equality ("and" equality)*
equality = comparison (("==" | "!=") comparison)*
comparison = term (("<" | "<=" | ">" | ">=") term)*
term = factor (("+" | "-") factor)*
factor = unary (("*" | "/" | "%") unary)*
unary = ("not" | "-")* call
call = primary ("." identifier "(" argument_list? ")" | "[" expression "]")*
primary = identifier
| number
| string
| boolean
| list
| "(" expression ")"
| static_method_call
static_method_call = identifier "." identifier "(" argument_list? ")"
list = "[" (expression ("," expression)*)? "]"
argument_list = expression ("," expression)*
identifier = [a-zA-Z_][a-zA-Z0-9_]*
number = [0-9]+("."[0-9]+)?
string = '"' [^"]* '"'
boolean = "True" | "False"SPL uses a recursive descent parser with the following characteristics:
- Top-Down: Parses from start symbol down to terminals
- Predictive: Uses lookahead to determine parse path
- Left-Recursive Elimination: Handles left recursion through iteration
- Operator Precedence: Implements precedence through grammar structure
- Input: Source code string
- Output: Stream of tokens
- Implementation: Regular expression-based tokenizer
- Features: Line tracking, error recovery
- Input: Token stream
- Output: Abstract Syntax Tree (AST)
- Implementation: Recursive descent parser
- Features: Precedence handling, error reporting
- Input: AST
- Output: Annotated AST
- Implementation: Tree traversal with symbol table
- Features: Type checking, scope resolution
- Input: Annotated AST
- Output: Program results
- Implementation: Tree-walking interpreter
- Features: Dynamic typing, method dispatch
- Variables: Stored in JavaScript objects (hash tables)
- Scoping: Lexical scoping with scope chain
- Garbage Collection: Automatic via JavaScript runtime
- Method Resolution: Dynamic dispatch with prototype chain
- Startup Time: ~2-3 seconds (Pyodide loading)
- Parse Time: O(n) where n is source code length
- Execution Time: O(m) where m is number of operations
- Memory Usage: Proportional to variable count and data size
- Modern Browsers: Chrome, Firefox, Safari, Edge
- Requirements: ES6 support, WebAssembly
- Limitations: No file system access, network restrictions
- Benefits: No installation, cross-platform, sandboxed
- Code Editor: CodeMirror with syntax highlighting
- Execution Engine: Pyodide-based Python interpreter
- Error Reporting: Real-time error display
- Variable Inspection: Runtime state visualization
- Variables:
camelCaseorsnake_case - Methods:
lowercase()with descriptive names - Constants: Use static methods for constant values
- Always terminate statements with semicolons
- Use braces for all control flow blocks
- Indent consistently (2 or 4 spaces)
- Break long chains across multiple lines
- Use meaningful intermediate variables for complex chains
- Prefer readability over brevity
# Good: Use appropriate data structures
numbers = List.range(1, 1000);
total = Math.sum(numbers);
# Avoid: Nested loops for simple operations
total = 0;
for i in range(1000) {
total = total + i;
}
# Good: Use built-in methods
words = text.split(" ");
result = words.join("-");
# Avoid: Manual string building
result = "";
# Complex manual concatenation...
if value.length() > 0 and value.contains("@") {
# Process valid email
} else {
print("Invalid email format");
}
result = data
.filter_valid()
.sort()
.slice(0, 10)
.join(", ");
index = items.index(target);
if index != -1 {
found_item = items[index];
} else {
print("Item not found");
}
if, else, while, for, in, break, True, False, and, or, not, range, print
print(...): Output values to consolerange(start?, end, step?): Generate numeric sequences
Common ASCII codes for String.fromcode():
- A-Z: 65-90
- a-z: 97-122
- 0-9: 48-57
- Space: 32
- Newline: 10
- π (pi): 3.141592653589793
- e (Euler's number): 2.718281828459045
This documentation covers SPL version 2.0. For the latest updates and examples, visit the online IDE.
Last Updated: June 2025