A programming language for math using postfix notation.
Component is the follow-up language to Basis which is also a math language. Component was built with insights learned from writing Basis. The main difference between the two is that Basis has a more complicated syntax which included types and data structures whereas Component uses postfix notation with the addition of functions as operations.
The code for Component and Basis use the same lexer. Component is mainly different because it has a different syntax. The lexer for basis was written generally enough that this code can be shared. It also helps that Basis is a math language inspired by postfix notation but not exclusively postfix notation.
Note that the symbol >
is used in code example to show that these expressions are being put into a REPL and evaluated. These symbols at the start of the expression are not apart of the syntax.
Component 0.1.0
A programming language for math using postfix notation
USAGE:
component [FLAGS] [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
-v, --verbose Print the stack and lexer information on each command
OPTIONS:
-a, --asm <asm> Use `x86` or `x86-64` to get x86-64 assembly and use `RISCV` or `RISC-V` for RISC-V
assembly
-f, --filename <filename> Specify an input Component file to be run
Component uses Postfix Notation. Postfix Notion has the arguments first, and then the function or operation. In the below example, we are adding 1 and 2 with the + operator to get 3. Try typing the following into the interactive prompt:
1 2 +
It should look this this once it's been typed in and you press enter:
> 1 2 +
-> 3
Component has a lot of the operators you would expect from math and other languages:
+ addition
- subtraction
* mltiplication
/ division
^ exponentiation
Some that may be familar from other languages:
! logical not
And some that may not be as familar:
? conditional assignment
Component also has functions that can be called in the same was as operators:
sqrt square root
And build in constants:
e Euler's number
C Speed of light in m/s
pi Pi
true and false
int
converts types into NumericIntLiteral if possibledec
converts types into NumericDecLiteral if possiblebool
converts types into BoolLiteral if possiblevars
prints all variablesstack
prints the current stack
e
Euler's numberpi
PiC
Speed of light in m/strue
Boolean truefalse
Boolean false
sqrt
Square root
+
addition-
subtraction*
multiplication/
division^
exponentiation!
logical not?
conditional assignment
Add two numbers together.
> 1 1 +
-> 2
This also works with -
for subtraction, *
for multiplication, and /
for division.
> 1 2 3 > 5 6 7 > x
-> 4 -8 4
Assign the value 2 to variable a
.
> 2 a =
-> 2
Use the variable a
.
a 4 *
-> 8
This will conditionally assign 5
to b
if a
is true.
> true a =
> 5 b a ?
Variables are statically typed in Component. Here is an example usage of a variable.
Create an addition function called foo
. Note that to start a function you do in fact need to use the >
again to tell the interpreter that the following is a function and should not be evaluated.
> > + foo func
> 1 2 foo
-> 3
Here is the example of caluclating the speed dilation of an object at velocity v
in m/s for time t
in seconds. More info about this in the section #Component-in-action
> > t 1 v 2 ^ C 2 ^ / - sqrt / s fn
> 4 5 /
-> 0.8
> 4 5 / int
-> 0
> 2 dec
-> 1
The following is a Component program to calculate the time dilation observed by an object moving at 300 m/s for 25 seconds.
After adding C as a constant and compressing this whole program into a single expression, here is the same code minus the variable assignment.
Here I created a function called s
that computes the same equation.
A test file with the same functionality can be found here: space.
Currently the interpreter creates x86-64 or RISC-V assembly as the code is being evaluated. In the future, an output file can be specified and the assembly will be put into that file and assembled.
Errors that occur in the interactive interpreter cause the line being interpreted to crash. When this happens, one of the following error messages will be displayed.
An Assignment Type Mismatch happens when you try to assign a value to an existing variable of a different type. In the following example, the variable a
is created as a NumericIntLiteral type. Then the value 0.8
is attempted to be assigned the variable a
but because 0.8
is of the type NumericDecLiteral, it fails with an Assignment Type Mismatch error. This means that variables are statically typed and cannot be changed during runtime.
> 1 a =
-> 1
> 4 5 / a =
Error: Assignment Type Mismatch [E1]
0.8 a =
^^^ cannot assign value 0.8 of type <NumericDecLiteral> to a variable of type <NumericIntLiteral>
A Wrong Type error happens when you try to call an operation or a function on one or more variables of the wrong type. Here is an example of an identifier being used before it has been assigned. Since it does not get swapped out for a value, since it hasn't been assigned, it has the type Identifier and therefore is the wrong type. In the future, this will also happen for math operations on the type String.
> a 1 +
Error: Wrong Type [E2]
a 1 +
^ value is not a <NumericIntLiteral> or <NumericDecLiteral>
An Invalid Type Cast happens when you try to cast from type to type but there is not an operation where this is possible. Similar to the Wrong Type error, the example below shows how an Identifier that has not been assigned a value, is being attempted to cast to both a NumericDecLiteral with the keyword dec
and a NumericIntLiteral with the keyword int
. Since there has been no assignment to the variable a
, it will not get swapped out for a value, and the type cannot be cast to either.
> a dec
Error: Invalid Type Cast [E3]
a dec
^^^ Cannot convert <Identifier> to <NumericDecLiteral>
> a int
Error: Invalid Type Cast [E3]
a int
^^^ Cannot convert <Identifier> to <NumericIntLiteral>
The Stack Empty error happens when the function or operation that has been called requires more arguments than are currently on the stack. This is an indication that not enough variables where provided. In the example below, a single NumericIntLiteral has been added to the stack and then the Addition operation has been called. This gives and error because the Addition operation requires two arguments.
> 1 +
Error: Stack Empty [E4]
The Operation Not Implemented error occurs when a non-identifier symbol has been parsed that has not gotten functionality yet. Symbol here meaning one or many characters (e.g. foo
or 123
). Since symbols that are not keywords or existing identifiers get read as identifiers (so long they follow the identifier rules), there are few things that trigger the Operation Not Implemented error. The one class of symbols that do cause this error are characters like #
and $
which have not been assigned any operation.
> #
Error: Operation Not Implemented [E5]
> $
Error: Operation Not Implemented [E5]
Here is an example of what this might look like in the interactive interpreter.
Component is named after vectors, where a single scalar in a vector is called a "component" of that vector (e.g. < 1 2 3 > where 1, 2, and 3 are all components. This is also in reference to the name Basis, where a basis vector is a subset of a vector space where the vectors are linearly independent.