/plainscript

A very plain scripting language

Primary LanguageJavaScriptMIT LicenseMIT

PlainScript

PlainScript is a very plain scripting language. Its only purpose is to serve as a starting point for student language design and implementation projects.

def sum_of_digits(n):
    if n < 0:
        return sum_of_digits(-n)
    elif n < 10:
        return n
    else:
        return sum_of_digits(n / 10) + (n % 10)

print(sum_of_digits(n: 8835299))

The Language

The language is described in some detail at the language home page.

A PlainScript Compiler

This project hosts a simple compiler that reads a PlainScript program from a file, translates it to JavaScript, and outputs the JavaScript code to standard output. It supports three options, -a (to display the abstract syntax tree then stop), -i (to display the analyzed semantic graph then stop), and -o (to turn optimizations on). Given the PlainScript program:

let x = 5 + 8
if true:
  print(-x)

invoking the compiler with the -a option outputs the abstract syntax tree for the program to standard output:

$ ./plainscript.js -a example.pls

produces

Program {
  statements:
   [ VariableDeclaration {
       ids: [ 'x' ],
       initializers:
        [ BinaryExpression {
            op: '+',
            left: NumericLiteral { value: 5 },
            right: NumericLiteral { value: 8 } } ] },
     IfStatement {
       cases:
        [ Case {
            test: BooleanLiteral { value: true },
            body:
             [ CallStatement {
                 call:
                  Call {
                    callee: IdentifierExpression { id: 'print' },
                    args:
                     [ Argument {
                         id: null,
                         expression: UnaryExpression { op: '-', operand: IdentifierExpression { id: 'x' } } } ] } } ] } ],
       alternate: null } ] }

The -i flag does semantic analysis, and writes out the decorated abstract syntax tree. So

$ ./plainscript.js example.pls -i

produces

Program {
  statements:
   [ VariableDeclaration {
       ids: [ 'x' ],
       initializers:
        [ BinaryExpression {
            op: '+',
            left: NumericLiteral { value: 5 },
            right: NumericLiteral { value: 8 } } ],
       variables: [ Variable { id: 'x' } ] },
     IfStatement {
       cases:
        [ Case {
            test: BooleanLiteral { value: true },
            body:
             [ CallStatement {
                 call:
                  Call {
                    callee:
                     IdentifierExpression {
                       id: 'print',
                       referent:
                        FunctionObject {
                          id: 'print',
                          params: [ Parameter { id: '_', defaultExpression: null } ],
                          body: null,
                          requiredParameterNames: Set { '_' },
                          allParameterNames: Set { '_' } } },
                    args:
                     [ Argument {
                         id: null,
                         expression:
                          UnaryExpression {
                            op: '-',
                            operand: IdentifierExpression { id: 'x', referent: Variable { id: 'x' } } } } ] } } ] } ],
       alternate: null } ] }

Finally, here’s an example performing a full translation to JavaScript:

$ ./plainscript.js example.pls
function print_1(_) {console.log(_);}
function sqrt_2(_) {return Math.sqrt(_);}
let x_3 = (5 + 8);
if (true) {
  print_1((- x_3));
}

You can use the optimization flag -o with our without the -i flag. In our little example, the optimizer will detect a constant folding opportunity and a conditional case that is always executed. The resulting JavaScript will be:

$ ./plainscript.js example.pls
function print_1(_) {console.log(_);}
function sqrt_2(_) {return Math.sqrt(_);}
let x_3 = 13;
print_1((- x_3));

The compiler does not (yet?) perform copy propagation and dead code elimination. That would be nice.