SMS (Symbolic Math System) is a math-focused scripting language for POSIX friendly environments. SMS has built-in functions for arithmetic, trigonometry, algebra, calculus, file access and more. SMS aims to become a simple general purpose programming language. SMS is still in early stages of development. SMS can be downloaded and installed from the Releases page on GitHub. The latest source code may have undocumented changes and this document is synchronized with the project at the point of the latest release:
In addition to providing a command line, SMS can interpret files.
Run sms -h
for command line options.
Unlike Javascript, Math functions in SMS are global keywords.
For example, taking a square root in javascript may look like Math.sqrt(x)
. In SMS, this same function is permanently available as sqrt(x)
.
Another major difference between SMS and javascript is that SMS provides the meta operator (:
) which allows developers to capture expressions for metaprogramming.
For example, taking a derivative of sin(x)
with respect to x
may be done with diff(:sin(x),:x);
where the colon prevents SMS from evaluating the input and ultimately preserving the symbolic expression.
The following is 'hello world' in SMS:
putLn("Hello world!");
SMS Versions have 3 numbers: the Major, Minor and Patch numbers.
Click on the chapter names below to expand the cheat sheet for that chapter.
Line comments start with #
.
Math
1. a + b ; # Add two numbers
2. a * b ; # Multiply two numbers
3. a - b ; # Subtract two numbers
4. a / b ; # Divide a by b
5. a ** b; # Raise a to the power of b
6. sin(x); cos(x); tan(x); # Trig functions
7. sinh(x); cosh(x); tanh(x); # Hyperbolic trig functions
8. sec(x); csc(x); cot(x); # Inverse trig funtions
9. sech(x); csch(x); coth(x); # Inverse hyperbolic trig functions
10. abs(x); # Return the absolute value of x
11. exp(x); # Euler's number, raised to the power of x
12. ln(x); # Natural log of x
13. log(b,x); # Log, base b of x
14. sqrt(x); # Square root of x
15. random(); # Generate a random number from 0 to 1
16. seed(number); # Seed the random generator based on an integer
17. round(); # Nearest integer
18. diff(:sin(x),:x); # Return the derivative of sin(x) with respect to x
19. simp(:expr); # Attempt to simplify the given expression
Equality
1. a == b # returns true if a is the same value as b, else, returns false
2. a > b # returns true if a is more than b, else, returns false
3. a < b # returns true if a is less than b, else ,returns false
4. a >= b # returns true if a is more than or equal to b, else ,returns false
5. a <= b # returns true if a is less than or equal to b, else ,returns false
6. true is true # the is keyword returns true if both objects are the same instance
Flow Control
1. return x ; # return this value from a function
2. twice = (x) => 2 * x; # make a function that doubles numbers
3. quad = (x,a,b,c) => a*x^2+b*x+c; # a quadratic function in x
4. { command1 ; command2 ; ... }; # A block of commands acts as 1 command
5. if(condition) command; # execute command if true, else return false
6. if(condition) command1 else command2; # if condition evaluates to true, executes command1, else executes command2
7. while(condition) statement # continually repeat statement until condition is false
8. for(let a = 0;a < 100;a = a + 1) putln(toStr(a)) # will print 0 to 100
8. doWhile ( condition ) statement # Repeating statement until condition is false (checking condition after running statement)
9. map( function, expression ) # return a new array where each element is the result of applying function to the correlating element of the given expression
10. ~x # if boolean is false, returns true, otherwise, returns false
11. EXPR || EXPR # returns true of either expression is true, else false
12. EXPR && EXPR # returns true of both expressions are true, else false
13. exit(n); # quit SMS and return this integer to the OS as the command return value
14. :sin(x); # capture any expression with the unary meta operator. Use parens to capture more.
Contexts
1. let var = value ; # creates a new variable in the current context with the given value.
2. rm var; # removes the variable from the current context.
3. var = value; # searches for var in this context, then up the parent path, and if found, sets to value, else a new variable is created in the current context.
4. let context = { var1 -> value1; var2 -> value2 }; # builds a context with 2 variables and saves it under the variable 'context'
5. context.var1; # Retreive the value of a specific variable from the context
6. context.var1 = value; # Set the value of a specific variable from the context
7. parent(context); # Return the parent scope of the provided context
8. cxLet(cx,:x,value); # A way to create a new variable in a context or just set it to a new value
9. cxSet(cx,:x,value); # A way to set cx.x=value for a context cx or return false
10. cxGet(cx,:x); # Get a value associated with the given key, or return false
11. cxGetFar(cx,:x,value); # Get the value, searching to higher scopes in the search
12. cxSize(cx); # Return the number of entries in this context
13. cxValues(cx); # Return an array with the values of the context
14. cxKeys(cx); # Return an array with the keys of the context
15. cxDot(cx,:symbol); # Return this variable from this context
16. cxContaining(cx,:key); # Returns the context which contains :key, by looking at cx and its ancetry
17. cxRm(cx, :var); # Remove this entry from the context
18. cxImport(cx1, cx2); # import the key/value pairs from cx1 into cx2. Overwrites existing key values
19. cxSetParent(cx1, cx2); # Set the parent of cx1 to cx2.
Arrays
1. [ expr1, expr2 ] # Create an array by evaluating expressions
2. :[ expr1 , expr2 ] # Create an array of unevaluated expressions
3. array[ i ] # Return the i'th element of the array, where i=0 is the first element
4. array[ i ] = value # Set the ith value of the array. Returns true upon success only
5. size( array ) # Return the number of elements in the array
6. size( expr ) # Return the number of arguments in the expression
Strings
1. let s = "example\nstring"; # s is now a string with a newline escape code (\n)
2. strFind(s,to_find); # Return the first location of to_find
3. strSize(s); # Return the length of string s
4. strEscape(s); # Convert any escape codes into their correlating character
5. "this" str+ "that"; # Return a string that is the concatenation of s1 with s2 in order
6. strPart(s1,start,len) # Return a part of the string, starting at index start, and with length len
7. toStr(object0); # Return the string representation of object0
8. input(); # Allow the user to enter a string of text, which becomes the return value
9. put(s1); # Print the string s1
10. putLn(s1); # Print the string s1 and go to the next line
11. strSplit(haystack,needle); # Return an array of strings which are the parts of haystack, split up by instances of needle
Files
1. fileReadStr("test.txt"); #reads test.txt , paths are relative to the working directory
2. fileWriteStr(fname, content); # takes a string for the file name to write to, and a string for the content to write
3. fileParse(fname); # Parses the file into a single object
4. cd("..") ; # change working directory
5. pwd(); # returns the working directory
6. ls() ; # show files and directories at the current working directory
7. import "filename.sms" # will find the file in PATH, execute it and return the result
Date and Time
1. date() # returns the date and time in the form of an array of 9 numbers, listed with their array index:
# date()[0]: seconds (0-60)
# date()[1]: minutes (0-59)
# date()[2]: hours (0-23)
# date()[3]: Day of month (1-31)
# date()[4]: months since January (0-11)
# date()[5]: Years since 1900
# date()[6]: Days since Sunday (0-6)
# date()[7]: Days since January 1 (0-365)
# date()[8]: Dayslights Savings flag (positive if daylight savings is in effect, 0 if not, negative if this is unknown)
2. time() # returns an array with 2 values: the number of seconds since January 1, 1970, then the number of microseconds since the last whole second.
3. dateStr() # returns the date in a 24 character string, like: "Thu Apr 6 01:20:24 2023"
4. sleep(n) # pause process execution for n milliseconds.
Processes
1. osFork(); # returns a process number. If the number is 0, we are in the child process. if the number is more than 0, we are in the same process as before, and the id is the id of the child process. If the number is -1, then forking failed. Check sms_src/fork.sms for an example.
2. osWait(); # If a child process is running, this waits until the process terminates, then returns an array with 2 elements: a process id, and the return code. If there is no child process to wait for, this function returns [-1,1]. Use this function from the parent of a child process made by osFork(). Check sms_src/fork.sms for an example.
3. osExec("path/to/binary"); # Execute another file, and wait until the file returns. The return value of osExec is the return value of the process.
4. exit(n) ; # exit SMS with return code specified by n
Expressions
1. xpOp(:(a+b)); # Returns the id of the operation in the expression2. xpSetOp(:(a+b),17) # Set the operator
3. xpOpStr(17) # Get the string for an operator
Functions
1. let myFunc = (x,y)=>sqrt(x**2+y**2); # Make a function that depends on x and y2. let f = fnSetParent(myFunc,parent(self)); # An example of changing the parent context of a function. Check fnSetParent.sms
3. fnXp((x)=>x**2); # Would return x**2 as an expression. same as :(x^2)
4. fnSetXp(funcion, :(expression)); # Mutate the function to have new content
5. fnParams(func); # returns the input params as an array of symbols
6. fnSetParams(func,(:x,:y,:z)); # change the parameter names for a function
7. fnParent(func); # returns the parent context of the function
8. fnSetParent(func, cx); # returns a new function with new parent
- At the Releases page , under 'Assets', you can find binary executable files for Linux, OpenBSD, and FreeBSD You may rename the file to 'sms' and copy to anywhere you need. On most Linux/Unix systems, copying to somewhere like /usr/local/bin directory is a fine way to install the program. The program is small and portable, so you can have copies where necessary.
Required Packages:
-
Flex
-
Bison
-
GCC or CLANG compiler
-
GNU Make ( or compatible build program )
The makefile uses clang by default. Edit the first lines of the makefile to use your preferred compiler and make program if necessary.
Once you have the necessary packages installed (GCC/Clang, Flex, and Bison), Change directory to the top level of the project (where LICENSE.txt is located) and run:
make
Upon success, you will have a portable executable called 'sms' in the bin directory.
On Linux/Unix systems, You may be able to build and install in one step:
sudo make install
This command builds the executable and copies it to /usr/local/bin/sms
- Mathematical evaluation. Commands like
a=4*sin(PI/8);
will evaluate to a double precision decimal number. - Copying garbage collector. Allows for the entire program memory to compact itself into a small contiguous block after each command.
- Lexical scope.
- SMS command flags allow for running scripts, changing memory heap size, immediate expression evaluation at the command line and more.
- Custom memory heap size can be set from 10 kilobytes to 4 terrabytes.
- User contexts (objects), which allow for the user to create a new context with values of all types including more contexts.
- Safe and fast internal string manipulations, with no character counting and no intermediate buffers for expression printing.
- Turing completeness.
- Recursive function calls.
- Local variables are implemented as array indices, making them relatively fast.
-
diff
command for automatic differentiation. Usediff(:(any_expression),:any_symbol)
and SMS will return the derivative ofany_expression
with respect toany_symbol
. -
simp
command for simplifying expressions. Usesimp(:(any_expression))
and SMS will return a simplified version ofany_expression
or it will return the input. - Example scripts are located in
sms_src
directory. - Process forking support. Use the osFork() command to build multiprocessing SMS programs. Also, use osExec to run other programs.
- Variable mapping uses mapped tries. Variable lookup times are reasonably fast and remain constant for any size context.
- Syntax errors are useful and line-specific, whether from terminal or interpreting a file.
- 'Last moment' garbage collection.
- Terminal history support
- Parser in C (instead of bison/flex)
- Allow parser extention functions.
- Manual (not just the cheat sheet).
- Design documentation.
- Instructions for adding primitive functions written in C
- Ability to call dynamic library functions
- Generational garbage collection
- Bytecode generation and execution
- Tail call optimization.
- Dynamic heap size, with min, max, and max_emptiness specs.
- An integral library for taking integrals.
- Arbitrary precision library.
- Simple web framework library
- A community!
- If you would like to contribute to this project and to the open source community, you can talk with me or even just clone the repo and come to me with a pull request, for which I can merge your work into a branch etc. It will be lovely when the contributor count is higher than 1. Any of the unchecked things in the plans above is an area welcome for contributions.