Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- function stack( )
- {
- this.inner = [ ];
- this.rstack = [ ];
- this.words = { };
- this.parsers = { };
- this.parsing = "";
- this.floating = /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/;
- this.define = function( name, func, parsing )
- {
- this.parsers[ name ] = parsing != null && parsing;
- if ( typeof( func ) == "function" )
- this.words[ name ] = func;
- else if ( typeof( func ) == "string" )
- this.words[ name ] = func.split( " " );
- else
- this.words[ name ] = func;
- return this;
- }
- this.parse = function( input )
- {
- return this.run( input.split( " " ) );
- }
- this.clear_parser = function( )
- {
- this.parsing = "";
- }
- this.run = function( words )
- {
- if ( words != null )
- for ( var index in words )
- this.push( words[ index ] );
- return this;
- }
- this.push = function( object )
- {
- if ( this.parsing == "" && typeof( object ) == "string" && object in this.parsers && this.parsers[ object ] )
- {
- this.inner.push( object );
- this.parsing = object;
- }
- else if ( this.parsing != "" )
- {
- this.inner.push( object );
- object = this.parsing;
- }
- if ( typeof( object ) == "string" && object in this.words && typeof( this.words[ object ] ) == "function" )
- this.words[ object ]( this.inner, this.rstack );
- else if ( typeof( object ) == "string" && object in this.words )
- this.run( this.words[ object ] );
- else if ( typeof( object ) == "string" && this.floating.exec( object ) )
- this.inner.push( parseFloat( object ) );
- else
- this.inner.push( object );
- return this;
- }
- this.pop = function( )
- {
- return this.inner.pop( );
- }
- }
- function grammar( )
- {
- var parser = new stack( );
- // Return parser with defined words
- return parser
- // Basic maths
- .define( "*", function( s ) { s.push( s.pop( ) * s.pop( ) ); } )
- .define( "+", function( s ) { s.push( s.pop( ) + s.pop( ) ); } )
- .define( "-", function( s ) { var a = s.pop( ); s.push( s.pop( ) - a ); } )
- .define( "/", function( s ) { var a = s.pop( ); s.push( s.pop( ) / a ); } )
- .define( "%", function( s ) { var a = s.pop( ); s.push( s.pop( ) % a ); } )
- .define( "float", function( s ) { s.push( parseFloat( s.pop( ) ) ); } )
- .define( "int", function( s ) { s.push( parseInt( s.pop( ) ) ); } )
- // Bitwise operators
- .define( "&", function( s ) { s.push( parseInt( s.pop( ) ) & parseInt( s.pop( ) ) ) } )
- .define( "|", function( s ) { s.push( parseInt( s.pop( ) ) | parseInt( s.pop( ) ) ) } )
- .define( "^", function( s ) { s.push( parseInt( s.pop( ) ) ^ parseInt( s.pop( ) ) ) } )
- .define( ">>", function( s ) { var a = parseInt( s.pop( ) ); s.push( parseInt( s.pop( ) ) >> a ) } )
- .define( "<<", function( s ) { var a = parseInt( s.pop( ) ); s.push( parseInt( s.pop( ) ) << a ) } )
- // Boolean logic
- .define( "true", function( s ) { s.push( true ); } )
- .define( "false", function( s ) { s.push( false ); } )
- .define( "=", function( s ) { s.push( s.pop( ) == s.pop( ) ); } )
- .define( "<", function( s ) { var a = s.pop( ); s.push( s.pop( ) < a ); } )
- .define( ">", function( s ) { var a = s.pop( ); s.push( s.pop( ) > a ); } )
- .define( ">=", function( s ) { var a = s.pop( ); s.push( s.pop( ) >= a ); } )
- .define( "<=", function( s ) { var a = s.pop( ); s.push( s.pop( ) <= a ); } )
- .define( "&&", function( s ) { var a = s.pop( ); s.push( s.pop( ) && a ); } )
- .define( "||", function( s ) { var a = s.pop( ); s.push( s.pop( ) || a ); } )
- // General
- .define( "2dup", "1 pick 1 pick" )
- .define( "max", "2dup < if swap then drop" )
- .define( "min", "2dup > if swap then drop" )
- // Basic stack manipulations
- .define( "pick", function( s ) { var index = parseInt( s.pop( ) ); s.push( s[ s.length - index - 1 ] ); } )
- .define( "roll", function( s ) { var index = parseInt( s.pop( ) ); var a = s.splice( s.length - index - 1, 1 ); s.push( a[ 0 ] ); } )
- .define( "pack", function( s ) { var length = parseInt( s.pop( ) ); var pack = s.splice( s.length - length, length ); s[ s.length ] = pack; } )
- .define( "drop", function( s ) { s.pop( ); } )
- .define( "depth", function( s ) { s.push( s.length ); } )
- // Standard FORTH words
- .define( "dup", "0 pick" )
- .define( "over", "1 pick" )
- .define( "swap", "1 roll" )
- // Vector/array words
- .define( "[", function( s, r ) { r.push( s.length ); } )
- .define( "]", function( s, r ) { var start = r.pop( ); parser.push( s.length - start ); parser.push( "pack" ); } )
- // Map words
- .define( "{", function( s, r ) { r.push( { } ); } )
- .define( "::", function( s, r ) { var n = s.pop( ); if ( n != "::" ) r[ r.length - 1 ][ n ] = s.pop( ); if ( n != "::" ) parser.clear_parser( ); }, true )
- .define( "@@", function( s ) { var n = s.pop( ); if ( n != "@@" ) s.push( s[ s.length - 1 ][ n ] ); if ( n != "@@" ) parser.clear_parser( ); }, true )
- .define( "}", function( s, r ) { s.push( r.pop( ) ); } )
- // Word definitions
- .define( ":", function( s, r )
- {
- function append( r, token )
- {
- // Initialise the state on the return stack
- if ( r.length == 0 || !( "name" in r[ r.length - 1 ] ) )
- r.push( { name: "", definition: [ ], count: 0 } );
- // For convenience, we'll grab a reference to the state here
- var state = r[ r.length - 1 ];
- // Deal with : and ; tokens
- if ( token == ":" )
- state.count ++;
- else if ( token == ";" )
- state.count --;
- // Drop opening :, assign name from first token following opening :, drop closing ;
- if ( token == ":" && state.count == 1 )
- ;
- else if ( state.count == 1 && state.name == "" )
- state.name = token;
- else if ( token != ";" || state.count > 0 )
- state.definition.push( token );
- // We are no longer consuming tokens here
- return state.count != 0;
- }
- // Keep appending until the closing ';' is encountered (allows nested word definitions)
- if ( !append( r, s.pop( ) ) )
- {
- // Stop parsing
- parser.clear_parser( );
- // Define the word
- var state = r.pop( );
- parser.define( state.name, state.definition );
- }
- },
- true )
- .define( ";", function( s ) { } )
- // Logic branching
- .define( "if", function( s, r )
- {
- function append( r, token )
- {
- // Initialise the state on the return stack
- if ( r.length == 0 || !( "if" in r[ r.length - 1 ] ) )
- r.push( { if: [ ], else: null, count: 0 } );
- // For convenience, we'll grab a reference to the state here
- var state = r[ r.length - 1 ];
- // Deal with if/else/then tokens
- if ( token == "if" )
- state.count ++;
- else if ( token == "else" && state.count == 1 )
- state.else = [ ];
- else if ( token == "then" )
- state.count --;
- if ( token == "if" && state.if.length == 0 )
- ;
- else if ( state.else == null )
- state.if.push( token );
- else
- state.else.push( token );
- // We are no longer consuming tokens here
- return state.count != 0;
- }
- // Keep appending until the closing 'then' is encountered
- if ( !append( r, s.pop( ) ) )
- {
- // Stop parsing
- parser.clear_parser( );
- // Execute the correct part of the condition
- parser.run( s.pop( ) ? r.pop( ).if : r.pop( ).else );
- }
- },
- true )
- .define( "else", function( s ) { } )
- .define( "then", function( s ) { } )
- // Loops
- .define( "do", function( s, r )
- {
- function append( r, token )
- {
- // Initialise the state on the return stack
- if ( r.length == 0 || !( "loop" in r[ r.length - 1 ] ) )
- r.push( { loop: [ ], count: 0 } );
- // For convenience, we'll grab a reference to the state here
- var state = r[ r.length - 1 ];
- // Deal with : and ; tokens
- if ( token == "do" )
- state.count ++;
- else if ( token == "loop" || token == "+loop" )
- state.count --;
- // Drop opening do
- if ( token == "do" && state.count == 1 )
- ;
- else
- state.loop.push( token );
- // We are no longer consuming tokens here
- return state.count != 0;
- }
- // Create the index stack if necessary
- if ( !( "istack" in parser ) )
- parser.istack = [ ];
- // Keep appending until the closing 'loop' is encountered (allows nested do loops)
- if ( !append( r, s.pop( ) ) )
- {
- // Stop parsing
- parser.clear_parser( );
- // Run the loop
- var i = s.pop( );
- var end = s.pop( );
- var state = r.pop( );
- while( i < end )
- {
- parser.istack.push( i );
- parser.run( state.loop );
- i = parser.istack.pop( );
- }
- }
- },
- true )
- .define( "i", function( s ) { s.push( parser.istack[ parser.istack.length - 1 ] ); } )
- .define( "loop", function( s ) { parser.istack.push( parser.istack.pop( ) + 1 ); } )
- .define( "+loop", function( s ) { parser.istack.push( parser.istack.pop( ) + s.pop( ) ); } )
- // Comments
- .define( "(", function( s, r )
- {
- function append( r, token )
- {
- // Initialise the state on the return stack
- if ( r.length == 0 || !( "count" in r[ r.length - 1 ] ) )
- r.push( { count: 0 } );
- // For convenience, we'll grab a reference to the state here
- var state = r[ r.length - 1 ];
- // Deal with : and ; tokens
- if ( token == "(" )
- state.count ++;
- else if ( token == ")" )
- state.count --;
- // We are no longer consuming tokens here
- return state.count != 0;
- }
- // Keep appending until the closing ')' is encountered (allows nested comments loops)
- if ( !append( r, s.pop( ) ) )
- {
- // Stop parsing
- parser.clear_parser( );
- }
- },
- true )
- .define( ")", function( s ) { } )
- // OpenJSCad words
- .define( "cube", function( s ) { s.push( cube( s.pop( ) ) ); } )
- .define( "cylinder", function( s ) { s.push( cylinder( s.pop( ) ) ); } )
- .define( "sphere", function( s ) { s.push( sphere( s.pop( ) ) ); } )
- .define( "torus", function( s ) { s.push( torus( s.pop( ) ) ); } )
- .define( "union", function( s ) { var a = s.pop( ); s.push( union( s.pop( ), a ) ); } )
- .define( "difference", function( s ) { var a = s.pop( ); s.push( difference( s.pop( ), a ) ); } )
- .define( "intersection", function( s ) { var a = s.pop( ); s.push( intersection( s.pop( ), a ) ); } )
- .define( "translate", function( s ) { var a = s.pop( ); s.push( s.pop( ).translate( a ) ); } )
- .define( "scale", function( s ) { var a = s.pop( ); s.push( s.pop( ).scale( a ) ); } )
- .define( "mirroredX", function( s ) { s.push( s.pop( ).mirroredX( ) ); } )
- .define( "mirroredY", function( s ) { s.push( s.pop( ).mirroredY( ) ); } )
- .define( "mirroredZ", function( s ) { s.push( s.pop( ).mirroredZ( ) ); } )
- // Debug
- .define( "dump", function( s ) { echo( "dump: " + s ); } )
- ;
- }
- function main( )
- {
- return grammar( )
- .parse( "{ 30 :: size true :: center } cube" )
- .parse( "{ 20 :: r true :: center } sphere" )
- .parse( "difference" )
- .parse( "{ 13 :: r true :: center } sphere" )
- .parse( "{ 21 :: size true :: center } cube" )
- .parse( "intersection" )
- .parse( "union" )
- .parse( "[ 0 0 15 ] translate" )
- .parse( ": plug { [ 0 0 0 ] :: start [ 0 0 10 ] :: end 1 :: r1 2 :: r2 50 :: fn } cylinder ;" )
- .parse( "plug" )
- .parse( "{ } torus" )
- .parse( "union" )
- .parse( "union" )
- .pop( );
- }
- function main( )
- {
- return grammar( )
- .parse( "( this is a comment )" )
- .parse( ": join depth 1 do union loop ;" )
- .parse( "10 1 do i cube [ i i i ] translate loop join" )
- .parse( "dup mirroredX join" )
- .parse( "dup mirroredY join" )
- .parse( "dup mirroredZ join" )
- .pop( );
- }
Add Comment
Please, Sign In to add comment