github.com/aretext/aretext@v1.3.0/syntax/languages/p4.go (about) 1 package languages 2 3 import "github.com/aretext/aretext/syntax/parser" 4 5 const ( 6 p4TokenRolePreprocessorDirective = parser.TokenRoleCustom1 7 p4TokenRoleAnnotation = parser.TokenRoleCustom2 8 ) 9 10 // P4ParseFunc returns a parse func for P4-16. 11 // See https://p4.org/p4-spec/docs/P4-16-v1.0.0-spec.html for the spec. 12 // See also p4.json for syntax highlighting rules: 13 // https://github.com/p4lang/p4-spec/blob/c84896fcd87f940983648b185ef9acf2b6f14838/p4-16/spec/p4.json 14 func P4ParseFunc() parser.Func { 15 return p4CommentParseFunc(). 16 Or(p4PreprocessorDirectiveParseFunc()). 17 Or(p4AnnotationParseFunc()). 18 Or(p4IdentifierOrKeywordParseFunc()). 19 Or(p4OperatorParseFunc()). 20 Or(p4StringParseFunc()). 21 Or(p4NumberParseFunc()) 22 } 23 24 func p4CommentParseFunc() parser.Func { 25 consumeLineComment := consumeString("//"). 26 ThenMaybe(consumeToNextLineFeed) 27 28 consumeBlockComment := consumeString("/*"). 29 Then(consumeToString("*/")) 30 31 return consumeLineComment. 32 Or(consumeBlockComment). 33 Map(recognizeToken(parser.TokenRoleComment)) 34 } 35 36 func p4PreprocessorDirectiveParseFunc() parser.Func { 37 directives := []string{ 38 "include", "if", "endif", "ifdef", 39 "define", "ifndef", "undef", "line", 40 } 41 return consumeCStylePreprocessorDirective(directives). 42 Map(recognizeToken(p4TokenRolePreprocessorDirective)) 43 } 44 45 func p4AnnotationParseFunc() parser.Func { 46 annotations := []string{ 47 "atomic", "defaultonly", "deprecated", "name", "noSideEffects", "noWarn", 48 "optional", "priority", "pure", "tableonly", "hidden", "globalname", 49 } 50 return consumeString("@"). 51 Then(consumeLongestMatchingOption(annotations)). 52 Map(recognizeToken(p4TokenRoleAnnotation)) 53 } 54 55 func p4IdentifierOrKeywordParseFunc() parser.Func { 56 isIdStart := func(r rune) bool { 57 return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || r == '_' || r == '$' 58 } 59 60 isIdContinue := func(r rune) bool { 61 return isIdStart(r) || (r >= '0' && r <= '9') 62 } 63 64 keywords := []string{ 65 "abstract", "action", "apply", "control", "default", "else", 66 "extern", "exit", "false", "if", 67 "package", "parser", "return", "select", "state", "switch", 68 "table", "this", "transition", "true", "type", "typedef", "value_set", "verify", 69 "bool", "bit", "const", "enum", "entries", "error", "header", "header_union", 70 "in", "inout", "int", "list", "match_kind", "out", "string", "tuple", "struct", "varbit", "void", 71 } 72 73 return consumeSingleRuneLike(isIdStart). 74 ThenMaybe(consumeRunesLike(isIdContinue)). 75 MapWithInput(recognizeKeywordOrConsume(keywords)) 76 } 77 78 func p4OperatorParseFunc() parser.Func { 79 return consumeLongestMatchingOption([]string{ 80 "=", ">", "<", "!", "~", "?", ":", 81 "==", "<=", ">=", "!=", "&&", "||", "++", 82 "+", "-", "*", "/", "&", "|", "^", "%", "<<", 83 ">>", "&&&", "..", 84 }).Map(recognizeToken(parser.TokenRoleOperator)) 85 } 86 87 func p4StringParseFunc() parser.Func { 88 return parseCStyleString('"', false) 89 } 90 91 func p4NumberParseFunc() parser.Func { 92 // NOTE: the number regex patterns in the spec's syntax highlighting definition (p4.json) 93 // differs from the spec itself (P4-16-spec.mdk). Follow the latter here. 94 consumeDigitsWithUnderscores := func(isDigit func(r rune) bool) parser.Func { 95 return func(iter parser.TrackingRuneIter, state parser.State) parser.Result { 96 var numUnderscores, numDigits uint64 97 for { 98 r, err := iter.NextRune() 99 if err != nil { 100 break 101 } else if r == '_' { 102 numUnderscores++ 103 } else if isDigit(r) { 104 numDigits++ 105 } else { 106 break 107 } 108 } 109 110 if numDigits == 0 { 111 return parser.FailedResult 112 } 113 114 return parser.Result{ 115 NumConsumed: numUnderscores + numDigits, 116 NextState: state, 117 } 118 } 119 } 120 121 isDecimalDigit := func(r rune) bool { return r >= '0' && r <= '9' } 122 isHexDigit := func(r rune) bool { 123 return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') 124 } 125 isOctalDigit := func(r rune) bool { 126 return r >= '0' && r <= '7' 127 } 128 isBinaryDigit := func(r rune) bool { 129 return r == '0' || r == '1' 130 } 131 132 consumeWidthPrefix := consumeRunesLike(isDecimalDigit). 133 Then(consumeSingleRuneLike(func(r rune) bool { return r == 'w' || r == 's' })) 134 135 consumeHex := consumeString("0"). 136 Then(consumeSingleRuneLike(func(r rune) bool { return r == 'x' || r == 'X' })). 137 Then(consumeDigitsWithUnderscores(isHexDigit)) 138 139 consumeOctal := consumeString("0"). 140 Then(consumeSingleRuneLike(func(r rune) bool { return r == 'o' || r == 'O' })). 141 Then(consumeDigitsWithUnderscores(isOctalDigit)) 142 143 consumeBinary := consumeString("0"). 144 Then(consumeSingleRuneLike(func(r rune) bool { return r == 'b' || r == 'B' })). 145 Then(consumeDigitsWithUnderscores(isBinaryDigit)) 146 147 consumeDecimalWithPrefix := consumeString("0"). 148 Then(consumeSingleRuneLike(func(r rune) bool { return r == 'd' || r == 'D' })). 149 Then(consumeDigitsWithUnderscores(isDecimalDigit)) 150 151 // Ensure first digit is not an underscore. 152 consumeDecimalWithoutPrefix := consumeSingleRuneLike(isDecimalDigit). 153 ThenMaybe(consumeDigitsWithUnderscores(isDecimalDigit)) 154 155 return consumeWidthPrefix. 156 MaybeBefore( 157 consumeHex. 158 Or(consumeOctal). 159 Or(consumeBinary). 160 Or(consumeDecimalWithPrefix). 161 Or(consumeDecimalWithoutPrefix)). 162 Map(recognizeToken(parser.TokenRoleNumber)) 163 }