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  }