Skip to content

Commit

Permalink
Add lexer for P4 (#2049)
Browse files Browse the repository at this point in the history
* Add lexer for P4

* ruby 2.7 compat

* add P4 to Languages.md
  • Loading branch information
rcgoodfellow authored Jul 4, 2024
1 parent c150ef9 commit 93e2bae
Show file tree
Hide file tree
Showing 5 changed files with 553 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/Languages.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
- OCL (`ocl`)
- OpenEdge ABL (`openedge`)
- OpenType Feature File (`opentype_feature_file`)
- P4 (`p4`)
- Pascal (`pascal`)
- Perl (`perl`)
- PHP (`php`)
Expand Down
99 changes: 99 additions & 0 deletions lib/rouge/demos/p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include <core.p4>
#include <v1model.p4>

const bit<16> TYPE_IPV4 = 0x800;
typedef bit<9> egressSpec_t;
typedef bit<48> macAddr_t;
typedef bit<32> ip4Addr_t;

header ethernet_t {
macAddr_t dstAddr;
macAddr_t srcAddr;
bit<16> etherType;
}

header ipv4_t {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> hdrChecksum;
ip4Addr_t srcAddr;
ip4Addr_t dstAddr;
}

struct metadata { /* empty */ }

struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
}

parser MyParser(packet_in packet,
out headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {

state start { transition parse_ethernet; }

state parse_ethernet {
packet.extract(hdr.ethernet);
transition select(hdr.ethernet.etherType) {
TYPE_IPV4: parse_ipv4;
default: accept;
}
}

state parse_ipv4 {
packet.extract(hdr.ipv4);
transition accept;
}

}

control MyIngress(inout headers hdr,
inout metadata meta,
inout standard_metadata_t standard_metadata) {
action drop() {
mark_to_drop(standard_metadata);
}

action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
standard_metadata.egress_spec = port;
hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
hdr.ethernet.dstAddr = dstAddr;
hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
}

table ipv4_lpm {
key = { hdr.ipv4.dstAddr: lpm; }
actions = { ipv4_forward; drop; NoAction; }
size = 1024;
default_action = drop();
}

apply {
if (hdr.ipv4.isValid()) {
ipv4_lpm.apply();
}
}
}


control MyDeparser(packet_out packet, in headers hdr) {
apply {
packet.emit(hdr.ethernet);
packet.emit(hdr.ipv4);
}
}

V1Switch(
MyParser(),
MyIngress(),
MyDeparser()
) main;
81 changes: 81 additions & 0 deletions lib/rouge/lexers/p4.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

module Rouge
module Lexers
class P4 < RegexLexer
tag 'p4'
title 'P4'
desc 'The P4 programming language'
filenames '*.p4'
mimetypes 'text/x-p4'

def self.keywords
@keywords ||= %w(
abstract action actions apply const default default_action else enum
entries extern exit if in inout key list out package packet_in
packet_out return size select switch this transition tuple type
typedef
)
end

def self.operators
@operators ||= %w(
\|\+\| \|-\| \? \& \&\&\& < > << >> \* \| ~ \^ - \+ /
\# \. = != <= >= \+\+
)
end

def self.decls
@decls ||= %w(
control header header_union parser state struct table
value_set
)
end

def self.builtins
@builtins ||= %w(
bit bool error extract int isValid setValid setInvalid match_kind
string varbit verify void
)
end

state :whitespace do
rule %r/\s+/m, Text
end

state :comment do
rule %r((//).*$\n?), Comment::Single
rule %r/\/\*(?:(?!\*\/).)*\*\//m, Comment::Multiline
end

state :number do
rule %r/([0-9]+[sw])?0[oO][0-7_]+/, Num
rule %r/([0-9]+[sw])?0[xX][0-9a-fA-F_]+/, Num
rule %r/([0-9]+[sw])?0[bB][01_]+/, Num
rule %r/([0-9]+[sw])?0[dD][0-9_]+/, Num
rule %r/([0-9]+[sw])?[0-9_]+/, Num
end

id = /[\p{XID_Start}_]\p{XID_Continue}*/
string_element = /\\"|[^"]/x

state :root do
mixin :whitespace
mixin :comment

rule %r/#\s*#{id}/, Comment::Preproc
rule %r/\b(?:#{P4.keywords.join('|')})\b/, Keyword
rule %r/\b(?:#{P4.decls.join('|')})\b/, Keyword::Declaration
rule %r/\b(?:#{P4.builtins.join('|')})\b/, Name::Builtin
rule %r/\b#{id}_[th]\b/x, Name::Class
rule %r/(?:#{P4.operators.join('|')})/x, Operator
rule %r/[(){}\[\]<>,:;\.]/, Punctuation
mixin :number
rule %r/@#{id}/x, Name::Label
rule %r/#{id}/x, Text
rule %r/"(?: #{string_element} )*"/x, Str::String
end
end
end
end
18 changes: 18 additions & 0 deletions spec/lexers/p4_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

describe Rouge::Lexers::P4 do
let(:subject) { Rouge::Lexers::P4.new }

describe 'guessing' do
include Support::Guessing

it 'guesses by filename' do
assert_guess :filename => 'foo.p4'
end

it 'guesses by mimetype' do
assert_guess :mimetype => 'text/x-p4'
end
end
end
Loading

0 comments on commit 93e2bae

Please sign in to comment.