import { Token } from './tokenizer' ; export interface WordTypeDict { [ type ] : Array < string > ; } export const wordTypes = { 'ARRAY' : [ 'lot' , 'lotta' ] , 'ARRAYEND' : [ 'stuff' ] , 'ASSIGNMENT' : [ 'be' , 'is' ] , 'BLOCK' : [ 'then' , 'piece' ] , 'BLOCKEND' : [ 'okay' ] , 'BLOCKCOMMENT' : [ 'listen' ] , 'BLOCKCOMMENTEND' : [ 'right' ] , 'COMMA' : [ 'and' ] , 'COMPEQ' : [ 'like' ] , 'COMPGREATER' : [ 'more' , 'mo' ] , 'COMPGREATEREQ' : [ 'over' ] , 'COMPLESS' : [ 'less' ] , 'COMPLESSEQ' : [ 'under' ] , 'COMPNOT' : [ 'ain\'t' , 'isn\'t' ] , 'CONDITIONELSE' : [ 'disagree' , 'disrespect' ] , 'CONDITIONIF' : [ 'sayin' , 'saying' ] , 'DECLARATION' : [ 'big' , 'lil' , 'those' , 'who' ] , 'DELETION' : [ 'rid' , 'ridda' ] , 'DELIMIT' : [ 'to' ] , 'FUNCTION' : [ 'business' ] , 'IGNORED' : [ 'cool' , 'fool' , 'got' , 'he' , 'her' , 'hey' , 'him' , 'his' , 'hot' , 'i' , 'in' , 'me' , 'my' , 'of' , 'our' , 'say' , 'says' , 'see' , 'she' , 'talk' , 'talks' , 'than' , 'that' , 'the' , 'their' , 'they' , 'think' , 'thinks' , 'up' , 'us' , 'we' , 'ya' , 'yall' , 'yo' , 'you' , 'your' ] , 'LINECOMMENT' : [ 'cuz' , 'so' ] , 'LOGICAND' : [ 'also' ] , 'LOGICNOT' : [ 'not' ] , 'LOGICOR' : [ 'or' ] , 'LOOPFOR' : [ 'rollin' , 'rolling' ] , 'LOOPWHILE' : [ 'always' , 'keep' ] , 'MATHMINUS' : [ 'smaller' ] , 'MATHPLUS' : [ 'bigger' ] , 'NEW' : [ 'make' ] , 'PAREN' : [ 'this' , 'these' ] , 'PARENEND' : [ 'well' ] , 'REFINE' : [ 'with' ] , 'REFINEEND' : [ 'yeah' ] , 'REFINEDOT' : [ 'get' , 'gotta' ] , 'RETURN' : [ 'rep' , 'represent' , 'show' ] , 'SEMICOLON' : [ 'uh' ] , 'THIS' : [ 'crib' , 'here' ] , 'VALNULL' : [ 'nah' ] , 'VALONE' : [ 'one' ] , 'VALTWO' : [ 'two' ] , 'VALUNDEFINED' : [ 'unreal' ] , 'VALZERO' : [ 'nothin' , 'nothing' ] } ; const unnestedTypes = [ 'BLOCKCOMMENT' , 'LINECOMMENT' ] ; interface ChildDict { [ type ] : string ; } const childrenTypes = { 'ARRAY' : 'ARRAYEND' , 'BLOCK' : 'BLOCKEND' , 'BLOCKCOMMENT' : 'BLOCKCOMMENTEND' , 'LINECOMMENT' : 'NEWLINE' , 'PAREN' : 'PARENEND' , 'REFINE' : 'REFINEEND' } ; interface WordDict { [ word ] : string ; } const words = ( function ( types ) : WordDict { let wordsDict = { } ; for ( const key in types ) { if ( ! types . hasOwnProperty ( key ) ) continue ; for ( const index in types [ key ] ) { if ( ! types [ key ] . hasOwnProperty ( index ) ) continue ; wordsDict [ types [ key ] [ index ] ] = key ; } } return wordsDict ; } ) ( wordTypes ) ; export interface Ast { body : Array < Node > ; type : string ; } const parse = function ( tokens , isDebug = false ) { let current = 0 ; if ( isDebug ) { console . log ( 'PARSER...' ) ; console . log ( `Parsing ${ tokens . length } tokens.` ) ; } function walk ( isNestable = true ) { let token = tokens [ current ] ; if ( isDebug ) { console . log ( `${ current + 1 }:` , token , isNestable ? '' : '(NOT NESTABLE)' ) ; } if ( ! token ) { return ; } if ( token . type === 'NEWLINE' ) { current ++ ; return createNode ( 'NEWLINE' , token . value ) ; } if ( token . type === 'SPACE' ) { current ++ ; return createNode ( 'SPACE' , token . value ) ; } if ( token . type === 'QUOTE' ) { current ++ ; return createNode ( 'QUOTE' , token . value ) ; } if ( token . type === 'NUMBER' ) { current ++ ; return createNode ( 'NUMBER' , token . value ) ; } if ( token . type == 'TOKEN' ) { current ++ ; return createNode ( 'TOKEN' , token . value ) ; } if ( token . type == 'WORD' ) { if ( ! token . value || ! words . hasOwnProperty ( token . value ) ) { current ++ ; return createNode ( 'NAME' , token . value ) ; } let type = words [ token . value ] ; if ( type === 'IGNORED' ) { current ++ ; return createNode ( 'IGNORED' , token . value ) ; } if ( ! childrenTypes . hasOwnProperty ( type ) ) { current ++ ; return createNode ( type , token . value ) ; } if ( ! isNestable ) { current ++ ; return createNode ( type , token . value ) ; } const node = createNode ( type , token . value , true ) ; node . children = node . children || [ ] ; const beginToken = token . value ; const endTokenType = childrenTypes [ type ] ; const canNest = ! ~ unnestedTypes . indexOf ( type ) ; current ++ ; let innerToken ; while ( type !== endTokenType ) { innerToken = walk ( canNest ) ; if ( ! innerToken ) { throw new Error ( `Failed to discover end token of type [${ endTokenType }] to match token [${ words [ beginToken ] } ${ beginToken }].` ) ; } node . children . push ( innerToken ) ; if ( innerToken . value ) { type = words [ innerToken . value ] || innerToken . type ; } else { type = innerToken . type ; } } return node ; } throw new Error ( `Parser had a problem. Unrecognized token: ${ token }` ) ; } const ast = { type : 'PROGRAM' , body : [ ] } ; while ( current < tokens . length ) { const node = walk ( ) ; if ( ! node ) { break ; } ast . body . push ( node ) ; } if ( isDebug ) { console . log ( 'PARSED AST: ' , ast ) ; } return ast ; } ; export interface Node { children : Array < Node > | null ; type : string ; value : string | null ; } function createNode ( type , value = null , hasChildren = false ) { const node = { children : null , type : type , value : value } ; if ( hasChildren ) node . children = [ ] ; return node ; } export default parse ;