import { ButteryFile , StructType , OneOfType , Field , Reference , VariableDeclaration , RPC , Channel , Statement , Service , } from "./ast" ; import { seq , sepBy , sepBy1 , map , maybe , Parser , apFirst , sat , either , eof , } from "parser-ts/lib/Parser" ; import { getMonoid } from "fp-ts/lib/Array" ; import { stream } from "parser-ts/lib/Stream" ; import { Token , BasicToken , NameToken , OpenBracketToken , CloseBracketToken , CommaToken , ColonToken , NewLineToken , ImportToken , QuotedStringToken , FromToken , ShiftInToken , ShiftOutToken , PeriodToken , nameTokenParser , } from "./lexer" ; import { NonEmptyArray } from "fp-ts/lib/NonEmptyArray" ; import { anyOf } from "./util" ; const matchToken = < T extends BasicToken < unknown > > ( tokenName ) => map ( ( x ) => x as T ) ( sat < Token > ( ( { token } ) => { return token === tokenName ; } ) ) ; const refNameParser = sepBy1 ( matchToken < PeriodToken > ( "period" ) , matchToken < NameToken > ( "name" ) ) ; export const refParser = seq ( refNameParser , ( tokenArr ) => map ( ( typeArgs ) => { if ( tokenArr . length > 2 ) { throw "References to namespaces of namespaces are not allowed" ; } const refToken = tokenArr . pop ( ) ! ; const nsToken = tokenArr . pop ( ) ; const ref = { ref : refToken . name , typeArgs } ; if ( nsToken ) { ref . namespace = nsToken . name ; } return ref ; } ) ( maybe ( getMonoid < Reference > ( ) ) ( apFirst ( matchToken < CloseBracketToken > ( "closebracket" ) ) ( seq ( matchToken < OpenBracketToken > ( "openbracket" ) , ( ) => sepBy1 ( matchToken < CommaToken > ( "comma" ) , refParser ) ) ) ) ) ) ; export const fieldParser = seq ( matchToken < NameToken > ( "name" ) , ( { name } ) => seq ( matchToken < ColonToken > ( "colon" ) , ( ) => { return map ( ( reference ) => ( { name , baseType : reference } ) ) ( refParser ) ; } ) ) ; const typeParamsParser = apFirst ( matchToken < CloseBracketToken > ( "closebracket" ) ) ( seq ( matchToken < OpenBracketToken > ( "openbracket" ) , ( ) => sepBy1 ( matchToken < CommaToken > ( "comma" ) , matchToken < NameToken > ( "name" ) ) ) ) ; const maybeTypeParamsParser = maybe ( getMonoid < NameToken > ( ) ) ( typeParamsParser ) ; type IndentingDeclaration = VariableDeclaration < Reference > ; const indentingDeclarationParser = < T > ( keywordTokenType , innerParser ) => { const declStartParser = seq ( matchToken < Token > ( keywordTokenType ) , ( ) => apFirst ( seq ( matchToken < ColonToken > ( "colon" ) , ( ) => seq ( matchToken < NewLineToken > ( "newline" ) , ( ) => matchToken < ShiftInToken > ( "shiftIn" ) ) ) ) ( seq ( matchToken < NameToken > ( "name" ) , ( name ) => map < NameToken [ ] , [ NameToken , NameToken [ ] ] > ( ( typeParams ) => { return [ name , typeParams ] ; } ) ( maybeTypeParamsParser ) ) ) ) ; const declEndParser = matchToken < ShiftOutToken > ( "shiftOut" ) ; return apFirst ( declEndParser ) ( seq ( declStartParser , ( [ name , typeParams ] ) => innerParser ( name , typeParams ) ) ) ; } ; export const structParser = indentingDeclarationParser ( "struct" , ( { name } , typeParams ) => map ( ( fields ) => { const struct = { type : "struct" , fields , typeParams : typeParams . map ( ( { name } ) => name ) } ; const decl = { statementType : "declaration" , name , value : struct } ; return decl ; } ) ( sepBy1 ( matchToken < NewLineToken > ( "newline" ) , fieldParser ) ) ) ; export const oneOfParser = indentingDeclarationParser ( "oneof" , ( { name } , typeParams ) => map ( ( fields ) => { const struct = { type : "oneof" , fields , typeParams : typeParams . map ( ( { name } ) => name ) } ; const decl = { statementType : "declaration" , name , value : struct } ; return decl ; } ) ( sepBy1 ( matchToken < NewLineToken > ( "newline" ) , fieldParser ) ) ) ; export const rpcParser = indentingDeclarationParser ( "rpc" , ( { name } , typeParams ) => { return map ( ( fields ) => { if ( typeParams . length !== 0 ) { throw "RPCs can't have type parameters" ; } const request = fields . find ( ( field ) => field . name === "request" ) ; const response = fields . find ( ( field ) => field . name === "response" ) ; if ( ! request ) { throw "Missing request!" ; } if ( ! response ) { throw "Missing response!" ; } if ( fields . length > 2 ) { throw "Too many fields!" ; } const rpc = { type : "rpc" , name : name , request , response } ; const decl = { statementType : "declaration" , name , value : rpc } ; return decl ; } ) ( sepBy1 ( matchToken < NewLineToken > ( "newline" ) , fieldParser ) ) ; } ) ; export const channelParser = indentingDeclarationParser ( "channel" , ( { name } , typeParams ) => { return map ( ( fields ) => { if ( typeParams . length !== 0 ) { throw "Channels can't have type arguments" ; } const incoming = fields . find ( ( field ) => field . name === "incoming" ) ; const outgoing = fields . find ( ( field ) => field . name === "outgoing" ) ; if ( ! incoming ) { throw "Missing request!" ; } if ( ! outgoing ) { throw "Missing response!" ; } if ( fields . length > 2 ) { throw "Too many fields!" ; } const rpc = { type : "channel" , name : name , incoming , outgoing } ; const decl = { statementType : "declaration" , name , value : rpc } ; return decl ; } ) ( sepBy1 ( matchToken < NewLineToken > ( "newline" ) , fieldParser ) ) ; } ) ; export const serviceParser = indentingDeclarationParser ( "service" , ( { name } , typeParams ) => { return map ( ( variables ) => { if ( typeParams . length !== 0 ) { throw "Services can't have type arguments" ; } const service = { type : "service" , name , variables } ; const decl = { statementType : "declaration" , name , value : service } ; return decl ; } ) ( sepBy1 ( matchToken < NewLineToken > ( "newline" ) , anyOf ( [ oneOfParser , structParser , rpcParser , channelParser ] ) ) ) ; } ) ; export const importParser = seq ( matchToken < ImportToken > ( "import" ) , ( ) => seq ( sepBy1 ( matchToken < CommaToken > ( "comma" ) , matchToken < NameToken > ( "name" ) ) , ( imports ) => { return map ( ( { contents } ) => { const importDecls = imports . map ( ( { name } ) => ( { statementType : "declaration" , name , value : { type : "import" , import : name , path : contents } } ) ) ; return importDecls ; } ) ( seq ( matchToken < FromToken > ( "from" ) , ( ) => matchToken < QuotedStringToken > ( "quotedString" ) ) ) ; } ) ) ; const statementParser = either < Token , Statement < Reference > [ ] > ( importParser , ( ) => map ( ( a ) => [ a ] ) ( anyOf ( [ oneOfParser , structParser , serviceParser ] ) ) ) ; const flatten = < T > ( unflattened ) : Array < T > => unflattened . reduce ( ( acc , cur ) => [ ... acc , ... cur ] , [ ] ) ; const statementsToFile = ( path ) => ( unflattened ) => { const statements = flatten ( unflattened ) ; return { path , variables : statements . filter ( ( { statementType } ) => statementType === "declaration" ) as VariableDeclaration < Reference > [ ] } ; } ; export const fileParser = ( path ) => apFirst < Token , void > ( eof ( ) ) ( map ( statementsToFile ( path ) ) ( sepBy ( matchToken < NewLineToken > ( "newline" ) , statementParser ) ) ) ; export function parse ( tokenStream , fname ) { return fileParser ( fname ) ( stream ( tokenStream , 0 ) ) ; }