simple-script-parser v1.2.9
Simple Script Parser
View on npm: https://www.npmjs.com/package/simple-script-parser View interpreter: https://github.com/danpaxton/simple-script-interpreter View language IDE: https://github.com/danpaxton/simple-script-ide
Parser
Given code as raw text, the parser converts it into an abstract syntax tree defined by an operator precedence and a set of grammar rules. The parser was built using the Parsimmon libray. Text is parsed left to right in top down manner. The parser is built from large parse functions built out of smaller parse functions. Specifically, the parser starts out parsing using simple regular expression parsers. Then, parses expressions using these regex parsers. Then, parses statements using expression parsers. Lastly, parses programs using statements parsers. Operator precedence is handled by building lower precedence operator parsers that use the next highest precedence operator parser as their main parse function. This allows the parser to bind to higher precedence operators before lower precedence operators. The variable checker is used to find cases of undeclared variables, duplicate parameters, valid statment use and invalid built-in function use. It accomplishes this by traversing the abstract syntax tree and maintaining a set of bound variables at each level of the program.
Installation
Clone repository,
$ git clone https://github.com/danpaxton/simple-script-parser.git`
$ cd simple-script-parser
Install and run tests,
$ npm install
$ npm run test
Or install package,
$ npm i simple-script-parser
Operator Precedence
The table below summarizes operator precedence from highest precedence to lowest precedence. Operators in the same box have the same precedence. Operators without syntax are binary. All operators group left to right except exponentiation which groups right to left.
Operator| Description
---:| ---
(expression),
{key: value...}
| Binding or parenthesized expression, collection display
x(...), x.attribute, x[...]
| call, reference, subscriptor
!x, ~x, ++x, --x, +x, -x
| logical not, bitwise not, pre-increment, pre-decrement, unary plus, unary negative
**
| exponentiation
*, /, %
| multiplication, division, remainder
+, -
| addition, subtraction
<<, >>
| shifts
&
| bit and
| | bit or
^
| bit xor
<, >, <=, >=, !=, ==
| comparisons
&&
| logical and
|| | logical or
... ? ... : ...
| ternary
Grammar
Below is a set of instructions that define valid statements and expressions for the simple script programming language. Simple-Script is a procedural and dynamically typed language. Each program consists of a series of statements that change the state of the program.
Lexical
type boolean := true | false
type integer := [+-]?[0-9]+
type float := [+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)? | Infinity | -Infinity
type string := (['"])([^'"]*)\1
type name := [a-zA-Z_$][a-zA-Z_$0-9]*
type none := null
Operators
type Unop := !, ~, ++, --, +, -
type Binop := *, /, %, **, +, -, <<, >>, |, &, |, ^, >=, <=, ==, !=, >, <, &&, ||
Expression
type Expr := { kind: 'none' }
| { kind: 'boolean', value: boolean }
| { kind: 'integer', value: integer }
| { kind: 'float', value: float }
| { kind: 'string', value: string }
| { kind: 'collection', value: { [ key: name | string | number ]: [ value: Expr ] } }
| { kind: 'variable', name: name }
| { kind: 'closure', params: name[], body: Stmt[], env: State }
| { kind: 'unop', op: Unop, expression: Expr }
| { kind: 'binop', op: Binop, e1: Expr, e2: Expr }
| { kind: 'call', fun: Expr, args: Expr[] }
| { kind: 'subscriptor', dict: Expr, expression: Expr }
| { kind: 'attribute', dict: Expr, attribute: name }
| { kind: 'ternary', test: Expr, trueExpr: Expr, falseExpr: Expr }
Statement
type Stmt := { kind: 'static', expression: Expr }
| { kind: 'assignment', assignArr: Expr[], expression: Expr }
| { kind: 'if', truePartArr : { test: Expr, part: Stmt[] }[], falsePart: Stmt[]}
| { kind: 'for', inits: Stmt[], test: Expr, updates: Stmt[], body: Stmt[] }
| { kind: 'while', test: Expr, body: Stmt[]] }
| { kind: 'delete', expression: Expr }
| { kind: 'return', expression: Expr }
Abstract
type State := { [key: name]: boolean | number | string | collection | closure | none }
type Program := { kind : 'ok', value : Stmt[] } | { kind : 'error', message : string }
Comments
Comments are specified using the //
characters.
Example,
// integer value.
x = 10;
Null values
The absence of a value is specified using the null
keyword.
Example,
a = null;
Boolean
Boolean values are represented using true
or false
.
Example,
a = true
, true assignment
if (false) { ... }
, conditional test
Numbers
Numbers are represented as integers, decimal point numbers, scientific notaion numbers, or infinity
Example,
1
, integer
-3.66
, float
2.67e-100
, scientific notation
-Infinity
, infinity
decimal point numbers, scientific notation numbers, and inifinity are all interpreted as floating point values.
Strings
Strings are represented as a sequence of ascii characters between a matching pair of single or double quotes.
Example,
''
, empty string
' str1 '
, single quotes
" str2 "
, double quotes
Strings can be subscripted at character positions.
'abc'[1]
is equivalent to 'b'
.
Operators
Define an expression using a binary or unary operator.
Unary Operators
Syntax,
unop expression
Example,
!x
, ++x
Binary Operators
Syntax,
expression binop expression
Example,
2 ** 8
, true && false
, a == b
Bitwise Operators
Bitwise Operators only operate on integers.
Example,
~5
, 1 >> 2
Error,
1.5 >> 2.5
Comparison chaining
Chaining comparsions will test each comparsion seperated by a logical AND (&&).
Example,
1 < 2 < 3
, is equivalent to 1 < 2 && 2 < 3
1 == 2 < 3 != 4
, is equivalent to 1 == 2 && 2 < 3 && 3 != 4
Built-in functions
Built in functions with default return values unless overwritten.
type(..)
, Returns the argument type
ord(..)
, Returns the ASCII value of character argument
abs(..)
, Returns the absolute value of number argument
len(..)
, Returns the length of argument collection or string
bool(..)
, Returns the boolean representation of the argument
int(..)
, Returns the greatest integer less than or equal to the argument
float(..)
, Returns the float representation of the argument
str(..)
, Returns the string represenation of the argument
print(.. , ...)
, Displays arguments to output.
Assignment statement
Assign a variable, or a collection attribute to an expression.
Syntax,
x = expression;
x.attribute = expression;
x[...] = expression;
Basic assignment
Example,
a = 1;
Assign multiple variables, or attributes the same value using an assignment chain.
a = b['key'] = c.val = 1;
Compound assignment
'+=' | '-=' | '*=' | '//=' | '/=' | '%=' | '<<=' | '>>=' | '&=' | '^=' | '|='
A variable must be defined before compound assignment.
Example,
a = 1; a += 1;
A compound assigment and the equivalent simple assignment will be parsed into the same result.
a += 1;
is the same as a = a + 1;
Assignment types cannot be mixed.
a = b += 1;
will result in a parse error.
Closures
Store function code, and lexical environment variables.
No parameters,
foo = () => { message = 'Hello'; return message; }; foo();
Single parameter,
foo = p => { return p + 1; }; foo(10);
Multiple parameter,
foo = (a, b, c) => { return a + b + c; }; foo(1, 2, 3);
Return line,
foo = (a, b) => { return a + b; };
, using return line foo = (a, b) => a + b;
Both methods will be parsed into the same result. Using no brackets allows only the one return statement.
Currying,
foo = a => b => c => a + b + c; foo(1)(2)(3);
Collections
Store a collection of attributes mapped to a value.
Empty,
data = {};
New attributes,
data = {}; data['key'] = 1; data.number = 10;
data
is now equivalent to { 'key': 1, 'number': 10 }
Names,
data = { a: 1, 2: true };
, is the same as data = { 'a': 1, '2': true };
.
Numbers,
data = { 1: 1, 2: true }
.
Strings,
data = { ' ': 1, 'key': true }
Only named attributes can be accesed using the reference ( x.attribute
) operator. Any attribute can be accesed using the subscriptor ( x[...]
) operator. All attributes are stored as strings, x[1]
is the same as x['1']
.
Example,
data = { 1: 1, key: true, ' ': false };
, access attribute 1
using data[1]
, attribute ' '
using data[' ']
and attribute key
using data.key
or data['key']
.
Ternary
Conditionally make decisions on the expression level. Defined by a test with a true expression and a false expression.
Syntax,
a = test ? true expression : false expression;
Ternary expressions can only contain expressions, use if statements for statement level conditionals.
Nested ternary must be within parentheses.
Example,
a = test1 ? ( test2 ? 1 : 3 ) : 2;
Parse error,
a = test1 ? test2 ? 1 : 3 : 2;
if
statement
Conditionally make decisions on the statement level. Defined by a series of tests with associated parts, and a false part.
Syntax,
if( test ) { part } else if( test ) { part } ... else { false part }
if
statements require brackets for more than one statement.
if only,
if(true) { 1 + 2; }
if else,
if(true) { 1 + 2; } else { 1 + 2; }
if else-if,
if(true) { 1 + 2; } else if(true) { 1 + 2; }
if else-if else,
if(true) { 1 + 2; } else if (true) { 1 + 2; } else { 1 + 2; }
while
statement
Loop until termination defined by a test expression.
Syntax,
while( test ) { body }
while
loops require brackets for more than one statement.
Example,
while(true) ++a;
for
statement
Loop with initializers. Performs updates at each iteration until termination defined by a test expression.
Syntax,
for( inits , test , updates ) { body }
for
statements require all parts. Require brackets for more than one statement.
Note, initializers must be assignments and update variables must be defined.
Example,
for(i = 0; i < 10; ++i) 2 + i;
for(i = 0, j = z = 1; i < 10; ++i, ++z, --j) { z += j; j += i}
Errors,
Need all parts,
for(i = 0; true; ) { 2 + i; }
for(; true; ++i) { 2 + i; }
z
not defined,
for(i = 0; i < 10; z = 0) i;
true
not an assignment statement,
for(i = 0, true; i < 10; ++i) i;
return
statement
Returns an expression from a function.
Syntax,
return expression;
Example,
return 1 + 2;
No return
statement or return;
, are both equivalent to return null;
.
delete
statement
Removes an attribute from a collection
Syntax,
delete expression;
Example,
a = { 1 : true, a : true };
delete a[1];
delete a.a;
Collection a
is now equivalent to {}
.
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago