6
\$\begingroup\$

Where it shines

FreezeFlame shines in readibility while still maintaining most of the C-like syntax. It also implements a few (Planning on several) operators that are still in testing for other languages.

Reason / TL;DR

I decided to write a new language, partially for the challenge and for the following reasons:

  • I wanted to implement the pipe operator: |>, the lambda operator: :: and the hash operator: #.
  • JavaScript comments were rather jagged, so I replaced /* with @@@
  • Accessing prototypes was a pain to type if you used it commonly, so .prototype. got replaced with ::. .prototype. will still work. I named it FreezeFlame because it implements two languages and styles.

Explanation

The language takes after both JavaScript and CoffeeScript in its syntax. A few other things have also been added. The following examples are before and after:

Live demo

Go to https://nolanlawson.github.io/jison-debugger/ and put the below grammar in the grammar section. Then, put in any of the tests. I chose that tester over https://zaa.ch/jison/try/ because it showed me the node output.

Array

Before:

[1,2,3]

After:

[1,2,3]

Switch

Before:

switch day {
   when 'tuesday' then {
      'tuesday' |> console.log;
      1;
   }
   else {
      return 2;
   }
}

After:

switch(day) {
      case 'tuesday': 
         console.log('tuesday');
         return 1;

      default: 
         return  2;

}

Object

Objects remain untouched from JS syntax.

String

Strings can be both multi line and single line. Before:

"""
    #{ false }
"""

After:

`
    ${ false }
`

Before: " #{ true } " After: ${ true }

Pipe

Increase readability for a nested function calls. Before:

a |> a |> #a

After:

this.a(a(a));

Function definition and call

Before:

() ~> {
   console.log(1)
}

After:

function() {
   return console.log(1);
};

Lambda

Access the prototype quickly. Before:

() -> {
   return String::split;
}

After:

()=> {
   return String.prototype.split;
};

#

# is equivalent to this or this. Before:

() -> {
    #;
}

After:

()=> {
   return this;    
};

Any requests can be put in comments. The following are proposed features:

  • <=>: Spaceship operator. Alias:
  • : Alias for the swap operator
  • : Alias for the function operator (Bound).
  • : Alias for the function operator (Un-bound).

The following have been implemented:

  • <->: Swap operator.

The code (grammar.jison):

%lex
%%

'@@@'(\s*(?:(?!\@){3,}.)*\s*)'@@@'
  {
      return 'COMMENT2'
  }
'@'([^\n]*)
  { 
      return 'COMMENT1'
  }
'"""'(\s*(?:[^\\]|(\\.)|(?!\'){3,})*\s*)'"""'
  {
      s = this.matches[1];
      yytext = s
      return 'STRING2'
  }
"'''"(\s*(?:[^\\]|(\\.)|(?!\'){3,})*\s*)"'''"
  {
      s = this.matches[1];
      yytext = s
      return 'STRING2'
  }
'"'((\\.)|(\\\n)|[^\"\n])*'"'
    {
        s = this.matches[1];
        yytext = s;
        return 'STRING1';
    }
"'"((\\.)|(\\\n)|[^\'\n]*)*"'"
    {
        s = this.matches[1];
        yytext = s;
        return 'STRING1'
    }
\n+             return 'NL'
([^\S\n]{3})+   return 'INDENT'
\t+             return 'INDENT'
[^\S\n]+        /* skip whitespace */
[0-9]+          return 'number'
'on'            return 'true'
'off'           return 'false'
'yes'           return 'true'
'class'         return 'class'
'no'            return 'false'
'true'          return 'true'
'false'         return 'false'
(return\s+)     return 'return1'
'null'          return 'null'
(return)        return 'return2'
'finally'       return 'finally'
'switch'        return 'switch'
'catch'         return 'catch'
'when'          return 'when'           
'else'          return 'else'
'then'          return 'then'
'for'           return 'for'
'var'           return 'var'
'try'           return 'try'
'if'            return 'if'
'isnt'          return '!=='
'this'          return 'THIS'
([a-zA-Z_][a-zA-Z_0-9]*)    return 'name'
"|>"            return '|>'
[-~]\>          return 'Glyph'
'@@@'           return '@@@'
'...'           return '...'
'>>>='          return '>>>='
'<<<='          return '<<<='
'>>>'           return '>>>'
'<<<'           return '<<<'
'==='           return '==='
'!=='           return '!=='
'<->'           return '<->'
'||'            return '||'
'!='            return '!='
'=='            return '=='
'>>'            return '>>'
'<<'            return '<<'
'&&'            return '&&'
'**'            return '**'
'::'            return '::'
'|='            return '|='
'+='            return '+='
'/='            return '/='
'-='            return '-='
'&='            return '&='
'++'            return '++'
'%='            return '%='
'#{'            return '#{'
'#'             return '#'
"'"             return "'"
'"'             return '"'
';'             return ';'
'>'             return '>'
'<'             return '<'
'^'             return '^'
','             return ','
'('             return '('
')'             return ')'
'['             return '['
']'             return ']'
'.'             return '.'
'`'             return '`'
'%'             return '%'
':'             return ':'
'&'             return '&'
'@'             return '@'
'='             return '='
'|'             return '|'
'+'             return '+'
'-'             return '-'
'/'             return '/'
'*'             return '*'
'{'             return '{'
'}'             return '}'
'?'             return '?'
<<EOF>>         return 'EOF'

/lex

%right '='
%left '+' '-'
%left '*' '/'

%start root

%%

root
    : Body EOF
        %{
          return $1
        }%
    ;
Body
    : Switch NL "Body"
        { $$ = $1 + $2 + $3 }
	| Switch
	| Expression NL OptIndent Body
	   { $$ = $1 + ';' + $2 + $3 + $4 }
	| Expression
	| Try NL "Body"
		{ $$ = $1 + $2 + $3 }
	| Try
	| For NL Body
	   { $$ = $1 + $2 + $3 }
	| For
	| Function OptNewLine Body
		{ $$ = $1 + $2 + $3 }
	| Function
	| Object NL Body
	   { $$ = $1 + $2 + $3 }
	| Object
	| Class NL Body
	   { $$ = $1 + $2 + $3 }
	| Class
	| MultiLineString NL Body
	   { $$ = $1 + $2 + $3 }
	| MultiLineString
	| Line NL OptIndent Body
		{ $$ = $1 + $2 + $3 + $4 }
	| BodyEnd OptNewLine -> $1 + $2
	| BodyEnd
	|
	   { $$ = '' }
	;
Return
	: OptIndent return1 Expression OptSemiColon
		{ $$ = $1 + $2 + ' ' + $3 + ';' }
	| OptIndent return2 OptSemiColon
		{ $$ = $1 + $2 + ';' }
	| OptIndent Expression OptSemiColon
		{ $$ = $1 + 'return ' + $2 + ';' }
	;
Class
   : class Name OptIndent '{' NL INDENT ClassBody OptIndent '}' OptSemiColon
  { $$ = 'class ' + $2 + ' {\n' + $6 + $7 + $8 + '\n};' }
   ;

ClassBody
   : name ParamDef OptIndent '{' NL INDENT Body OptIndent '}' NL INDENT ClassBody
      { $$ = $1 + $2.split('this.').join`` + ' {' + $2.replace(/^\(/,'').replace(/\)$/,'').split(',').filter(a=>a.includes('this.')).map(a=>a.split(/\s*=\s*/)[0]).map(a=>a+ ' = ' + a.replace(/\s*this\./,'') + ';').join`\n` + $6 + $Body + $8 + '}\n' + $11 + $12 }
   | name ParamDef OptIndent '{' NL INDENT Body OptIndent '}' NL
  { $$ = $1 + $2.split('this.').join`` + ' {' + $2.replace(/^\(/,'').replace(/\)$/,'').split(',').filter(a=>a.includes('this.')).map(a=>a.split(/\s*=\s*/)[0]).map(a=>a+ ' = ' + a.replace(/\s*this\./,'') + ';').join`\n` + $6 + $Body + $8 + '}\n' }
   ;
Line
	: OptIndent Expression OptSemiColon
		{ $$ = $1 + $2 + ';' }
	;
BodyEnd
	: Return
	| INDENT Expression OptSemiColon
	   { $$ = $1 + $2 + ';' }
	| Expression OptSemiColon -> $1 + ';'
	;
OptSemiColon
	: OptSemiColon OptSemiColon
	| ';'
	| 
		{ $$ = '' }
	;
Ternary
	: 'if' Expression 'then' Expression 'else' Expression
		{ $$ = $2 + ' ? ' + $4 + ' : ' + $6 }
	;
OptNewLine
	: NL
	| 
		{ $$ = '' }
	;
OptIndent
	: INDENT
	| 
		{ $$ = '' }
	;
OptComma
	: ','
	|
		{ $$ = '' }
	;
Function
	: FunctionStart '{' OptNewLine Body OptNewLine OptIndent '}' OptSemiColon
		{ $$ = $1 + ' {\n' + $Body + '\n}' }
	| FunctionStart '{ OptNewLine }' OptSemiColon
		{ $$ = $1 + '{' + $3 + '}' }
	;
FunctionStart
	: ParamDef Glyph
		%{
		  switch($2) {
			case '~>':
			  $$ =  'function' + $1;
			  break;
			case '->':
			  $$ =  $1 + ' =>';
			  break;
		  }
		}%
	;
Boolean
	: 'true'  -> 'true'
	| 'false' -> 'false'
	;
Param
	: '( )'			 -> '()' 
	| '(' List ')'	 -> '(' + $List + ')'
	;
List
	: Value ',' List
		{ $$ = $1 + ',' + $3 }
	| Name ',' List
	   { $$ = $1 + ',' + $3 }
	| '...' Value ',' List
		{$$ = '...' + $Value + ',' + $List }
	| '...' Value
		{ $$ = '...' + $3 }
	| '...' Name
	   { $$ = '...' + $2 }
	| Value
	| Name
	| Expression
		{$$ = $1}
	;
ParamDef
	: '( )'						-> '()' 
	| '(' ParamDefList ')'	-> $1 + $2 + $3
	;
ParamDefList
	: Name '... ,' ParamDefList
		{ $$ = $1 + $2 + $3 + $4 }
	| Name '=' Expression ',' ParamDefList
	   { $$ = $1 + ' = ' + $3 + ', ' + $5 }
 	| '(' Expression ')'
	| Name ',' ParamDefList 
		{ $$ = $1 + $2 + $3 }
	| Name '...'
		{ $$ = $1 + $2 }
	| Name '=' Expression
	   { $$ = $1 + ' = ' + $3 }
	| Name
		{ $$ = $1 }
	;
Expression
	: ERRORS
    | Name '<->' Name
		{ $$ = `[${$1}, ${$3}] = [${$3}, ${$1}]` }
	| Name '=' Switch
		{ $$ = $1 + $2 + '(function(){\n		' + $3 + '\n' + '})()' }
	| OptVar Name '=' Expression
		{$$ = `${$1.length?$1+' ':''}${$2} = ${$4}` }
    | Name '=' Expression
 		{ $$ = $1 + $2 + $3 }
	| Name ComboMath Expression
		{ $$ = $1 + $2 + $3 }
	| Expression Math Expression
		{ $$ = $1 + $2 + $3 }
	| Expression Math Expression
 		{ $$ = $1 + $2 + $3 }
	| Expression Operator Expression
		{ $$ = $1 + $2 + $3 }
	| Name '++'
	   { $$ = $1 + $2 } 
	| Name List
		{$$ = `${$1}(${$2})`}
	| Name Param
		{$$ = $1 + $2}
	| Value 'is' Name
		{$$ = `${$1} instanceof ${$3}`}
	| Ternary
    | Function
	| '(' Expression ')'
		{ $$ = $1 + $2 + $3 }
	| For
	| Switch
	| Value
	| Name
	| Comment
	| Pipe
	;
Comment
	: COMMENT1
		{ $$ = $1.replace(/^@/, '//') }
	| COMMENT2
		{ $$ = $1.replace(/^@@@/, '/*').replace(/@@@/g, '*/'); }-
	;
Operator
	: '&&'
	| '||'
	| '!==' -> '!=='
	| '===' -> '==='
	| '=='
	| '>='
	| '<='
	| '!='
	;
ComboMath
	: '|='
	| '*='
	| '/='
	| '%='
	| '+='
	| '-='
	| '&='
	| '>>>='
	| '>>='
	| '<<='
	;
Math
	: '>>>'
	| '**'
	| '<<'
	| '>>'
	| '&'
	| '^'
	| '|'
	| '+'
	| '*'
	| '+'
	| '-'
	| '/'
	| '<'
	| '>'
	| '%'
	;
OptVar
    : 'var'
    |
       { $$ = '' }
    ;
Value
	: Function
	| Boolean
	| Number
	| Pipe
	| String
	| Array
	| Null
	;
For
   : for '(' Expression ';' Expression ';' Expression ')' '{' OptNewLine Body OptNewLine OptIndent '}' OptSemiColon
  { $$ = $1 + '(' + $3 + $4 + $5 + $6 + $7 + $8 + $9 + $10 + $11 + $12 + $13 + $14 }
   ;
LineList
   : OptIndent Expression ',' OptNewLine LineList
  { $$ = $1 + $2 + ',\n' + $LineList }
   | OptIndent Expression
  { $$ = $1 + $2 }
   ;
Array
	: '[' OptNewLine ArrList OptNewLine ']'
	   { $$ = $1 + '\n' + $3 + '\n' + $5 }
	| '[' NL LineList NL ']'
		{ $$ = $1 + '\n' + $3 + '\n' + $5 } 
	| '[ ]'
		{ $$ = '[]' }
	;
KeyList
	: Key
		{ $$ = $1 + ': ' + $1 }
	| Value
		{ $$ = "'" + $1 + "'" + ': ' + $1 }
	;
Switch
	: switch Value  OptIndent '{' OptIndent NL WhenList OptNewLine OptIndent '}' OptIndent OptSemiColon
		{ $$ = $switch + '(' + $Value + ') {\n' + $WhenList + '};' }
	| switch Name '{' NL WhenList OptNewLine OptIndent '}' OptSemiColon
		{ $$ = $switch + '(' + $Name + ') {\n' + $WhenList + '};' }
	;
WhenList
	: OptIndent when WhenArr then '{' OptNewLine Body OptNewLine OptIndent '}' OptNewLine WhenList
		{ $$ = $OptIndent + '   ' + $WhenArr + '   ' + $Body + '\n' + $WhenList }
	| OptIndent when WhenArr then '{' OptNewLine Body OptNewLine OptIndent '}' OptNewLine
		{ $$ = $OptIndent + '   ' + $WhenArr + '   ' + $Body + '\n' + $OptIndent + '   break;' }
	| OptIndent else '{' OptNewLine Body OptNewLine OptIndent '}'
		{ $$ = $OptIndent + 'default: \n' + $Body }
	;
WhenArr
	: Expression ',' WhenArr
		{ $$ = 'case ' + $1 + ': \n' + $3 } 
	| Expression
		{ $$ = 'case ' + $1 + ': \n' }
    ;

Object
    : '{' OptNewLine OptIndent AssignList OptNewLine '}'
        { $$ = $1 + '\n' + $3 + $4 + '\n' + $6 }
	| '{' KeyList '}'
		{ $$ = $1 + '\n' + $2 + '\n}' }
	| NL NL AssignList NL
		{ $$ = '{\n' + $3 + '\n}' }
	| NL AssignList
		{ $$ = '{\n' + $2 + '\n}' }
	| AssignList
		{ $$ = '{\n' + $1 + '\n}' }
	| '{ }'
	   { $$ = '{}' }
	;
AssignList
	: OptNewLine OptIndent Key ':' Expression ',' AssignList
		{ $$ = $1 + $2 + $3 + ': ' + $5 + ',' + $7 }
	| OptNewLine OptIndent Key ':' Expression
	   { $$ = '\n' + $2 + $3 + ': ' + $5 }
	;
Try
	: OptIndent try '{' OptNewLine Body OptNewLine OptIndent '}' catch ParamDef '{' OptNewLine Body OptNewLine OptIndent '}' finally '{' OptNewLine Body OptNewLine OptIndent '}' OptSemiColon
		{ $$ = $1 + $try + ' {\n' + $1 + '	' + $5 + '\n' + $1 + '} catch' + $ParamDef + ' {\n' + $1 + '	' + $31 + '\n' + $1 + '} finally {\n' + $1 + '	' + $20 + '\n' + $1 + '}' }
	| OptIndent try '{' OptNewLine Body OptNewLine OptIndent '}' catch ParamDef '{' OptNewLine Body OptNewLine OptIndent '}' OptSemiColon
		{ $$ = $1 + $try + ' {\n' + $1 + '	' + $6 + '\n' + $1 + '} catch' + $ParamDef + ' {\n' + $1 + '	' + $13 + $1 + '\n};' }
	;
MultiLineString
   : STRING2
  { 
     function interpolate(str) {
       let current = str;
       str = str.replace(/\#\{(.*)\}/, function(_,match2) { return '${' + match2 + '}'});
       while(str != current) {
         current = str;
         str = str.replace(/\#\{(.*)\}/, function(_,match2) { return '${' + match2 + '}'});
       }
       return str;
     }
     $$ = "`" + interpolate($1.replace(/((^''')(\s*(?:(\\.)|(?!\'){3,}.)+\s*)('''$)|(^""")(\s*(?:(?!\'){3,}.)+\s*)("""$))/, function(_,Match1, Match2) { return Match1||Match2 })) + "`" }
   ;
String
	: STRING1
		{ 
		   function interpolate(str) {
       let current = str;
       str = str.replace(/\#\{(.*)\}/, function(_,match2) { return '${' + match2 + '}'});
       while(str != current) {
         current = str;
         str = str.replace(/\#\{(.*)\}/, function(_,match2) { return '${' + match2 + '}'});
       }
       return str;
     }
     $$ = "'" + interpolate($1) + "'" }
	;
Key
   : String
   | Name
   | Number
   | '[' Name ']'
  { $$ = $1 + $2 + $3 }
   | '[' Primitive ']'
  { $$ = $1 + $2 + $3 }
   ;
Primitive
   : Number
   | String
   | Boolean
   ;
Access
   : '[' String ']'
  { $$ = $1 + $2 + $3 }
   | '[' Name   ']'
  { $$ = $1 + $2 + $3 }
   | '.' Name
  { $$ = $1 + $2 }
   ;
Name
	: name Access -> $1 + $2
	| name
	| '#' name -> 'this.' + $2
	| '#'
		{ $$ = 'this' }
	| Name '::' name
		{$$ = $1 + '.prototype.' + $3}
	| Name '::'
		{ $$ = $1 + '.prototype' }
	;
Number
    : number
	;
Pipe
	: Expression '|>' Name
		{$$ = $Name + '(' + $Expression + ')' }
	| Expression '|>' '#' Name
		{$$ = 'this.' + $Name + '(' + $Expression + ')' }
	| Pipe '|>' Name
		{$$ = $3 + '(' + $1 + ')' }
	| Pipe '|>' '#' Name
		{$$ = 'this.' + $4 + '(' + $1 + ')' }
    ;
\$\endgroup\$
  • \$\begingroup\$ Do you consider using ragel? \$\endgroup\$ – sineemore Aug 14 '18 at 15:56
  • 1
    \$\begingroup\$ Is your code a compiler or a piece of example code? If the latter, how should we run it? \$\endgroup\$ – Mast Aug 14 '18 at 16:02
  • 1
    \$\begingroup\$ Are you sure that multiline comments are working? It is parsing @@@ as "//@@". \$\endgroup\$ – hjpotter92 Sep 14 '18 at 8:33
  • 1
    \$\begingroup\$ I'm not really sure your comments work as intended... I tried @@@comment start\na=1\ncomment end@@@, expecting it to be transformed into /*comment start\na=1\ncomment end*/. However, I got //@@comment start;\na=1;\ncomment(end(//@@)), so it seems that multiline comments aren't supported and that normal end-of-line comments have transformation issues (getting placed as argument to function call?). \$\endgroup\$ – hoffmale Jan 24 at 6:54
  • 1
    \$\begingroup\$ That just fixed the first issue (multiline comments), not the transformation issues. Input @comment start\na = 1\ncomment end@ gets transformed to //comment start;\na=1;\ncomment(end(//)). Notice the semicolon after start in the first line, and the double slash inside the parentheses on the last line! \$\endgroup\$ – hoffmale Jan 24 at 17:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.