diff --git a/lang/p4-macro/src/lib.rs b/lang/p4-macro/src/lib.rs index 9a62d4bf..90ac8d5b 100644 --- a/lang/p4-macro/src/lib.rs +++ b/lang/p4-macro/src/lib.rs @@ -1,7 +1,10 @@ use std::fs; +use std::sync::Arc; use p4::check::Diagnostics; -use p4::{check, error, error::SemanticError, lexer, parser, preprocessor}; +use p4::{ + ast::AST, check, error, error::SemanticError, lexer, parser, preprocessor, +}; use proc_macro::TokenStream; use serde::Deserialize; use serde_tokenstream::ParseWrapper; @@ -58,25 +61,42 @@ fn generate_rs( ) -> Result { //TODO gracefull error handling - let contents = fs::read_to_string(filename).unwrap(); + let mut ast = AST::default(); + process_file(Arc::new(filename), &mut ast, &settings)?; - let ppr = preprocessor::run(&contents).unwrap(); - let lines: Vec<&str> = ppr.lines.iter().map(|x| x.as_str()).collect(); - let lxr = lexer::Lexer::new(lines.clone()); - let mut psr = parser::Parser::new(lxr); - let mut ast = psr.run().unwrap(); - let (hlir, diags) = check::all(&ast); - check(&lines, &diags); - p4_rust::sanitize(&mut ast); - let tokens = p4_rust::emit_tokens( + let (hlir, _) = check::all(&ast); + + let tokens: TokenStream = p4_rust::emit_tokens( &ast, &hlir, p4_rust::Settings { - pipeline_name: settings.pipeline_name, + pipeline_name: settings.pipeline_name.clone(), }, - ); + ) + .into(); + + Ok(tokens) +} - Ok(tokens.into()) +fn process_file( + filename: Arc, + ast: &mut AST, + settings: &GenerationSettings, +) -> Result<(), syn::Error> { + let contents = fs::read_to_string(&*filename).unwrap(); + let ppr = preprocessor::run(&contents, filename.clone()).unwrap(); + for included in &ppr.elements.includes { + process_file(Arc::new(included.clone()), ast, settings)?; + } + + let (_, diags) = check::all(ast); + let lines: Vec<&str> = ppr.lines.iter().map(|x| x.as_str()).collect(); + check(&lines, &diags); + let lxr = lexer::Lexer::new(lines.clone(), filename); + let mut psr = parser::Parser::new(lxr); + psr.run(ast).unwrap(); + p4_rust::sanitize(ast); + Ok(()) } // TODO copy pasta from x4c diff --git a/p4/examples/bad/parser/badness-included.p4 b/p4/examples/bad/parser/badness-included.p4 new file mode 100644 index 00000000..6a965c9c --- /dev/null +++ b/p4/examples/bad/parser/badness-included.p4 @@ -0,0 +1,2 @@ +#include +#include diff --git a/p4/examples/codegen/hub.p4 b/p4/examples/codegen/hub.p4 index 717422d9..4a16e8ac 100644 --- a/p4/examples/codegen/hub.p4 +++ b/p4/examples/codegen/hub.p4 @@ -1,4 +1,4 @@ -#include +#include SoftNPU( parse(), diff --git a/p4/examples/codegen/softnpu.p4 b/p4/examples/codegen/softnpu.p4 index b622d521..5b3c8895 100644 --- a/p4/examples/codegen/softnpu.p4 +++ b/p4/examples/codegen/softnpu.p4 @@ -1,23 +1,15 @@ struct IngressMetadata { bit<8> port; + bool nat; + bit<16> nat_id; } struct EgressMetadata { bit<8> port; + bit<128> nexthop; + bool drop; } -parser NpuParser( - packet_in pkt, - out H parsed_headers -); - -control NpuIngress( - inout H hdr, - inout IngressMetadata ingress_meta, - inout EgressMetadata egress_meta, -); - -package SoftNPU( - NpuParser p, - NpuIngress ingress, -); +extern Checksum { + bit<16> run(in T data); +} diff --git a/p4/src/ast.rs b/p4/src/ast.rs index ffb68e7a..f2926ba9 100644 --- a/p4/src/ast.rs +++ b/p4/src/ast.rs @@ -723,6 +723,7 @@ impl Lvalue { kind: self.token.kind.clone(), line: self.token.line, col: self.token.col + parts[0].len() + 1, + file: self.token.file.clone(), }, } } @@ -734,6 +735,7 @@ impl Lvalue { kind: self.token.kind.clone(), line: self.token.line, col: self.token.col, + file: self.token.file.clone(), }, } } diff --git a/p4/src/error.rs b/p4/src/error.rs index 4ca78550..98254a60 100644 --- a/p4/src/error.rs +++ b/p4/src/error.rs @@ -1,6 +1,7 @@ -use crate::lexer::Token; +use crate::lexer::{Kind, Lexer, Token}; use colored::Colorize; use std::fmt; +use std::sync::Arc; #[derive(Debug)] pub struct SemanticError { @@ -16,31 +17,19 @@ pub struct SemanticError { impl fmt::Display for SemanticError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let loc = format!("\n[{}:{}]", self.at.line + 1, self.at.col + 1) + let loc = format!("[{}:{}]", self.at.line + 1, self.at.col + 1) .as_str() .bright_red(); - writeln!(f, "{} {}\n", loc, self.message.bright_white())?; + writeln!( + f, + "{}\n{} {}\n", + self.message.bright_white(), + loc, + *self.at.file, + )?; writeln!(f, " {}", self.source)?; - // The presence of tabs makes presenting error indicators purely based - // on column position impossible, so here we iterrate over the existing - // string and mask out the non whitespace text inserting the error - // indicators and preserving any tab/space mixture. - let carat_line: String = self - .source - .chars() - .enumerate() - .map(|(i, x)| { - if i == self.at.col { - return '^'; - } - if x.is_whitespace() { - x - } else { - ' ' - } - }) - .collect(); + let carat_line = carat_line(&self.source, &self.at); write!(f, " {}", carat_line.bright_red()) } } @@ -64,28 +53,16 @@ impl fmt::Display for ParserError { let loc = format!("[{}:{}]", self.at.line + 1, self.at.col + 1) .as_str() .bright_red(); - writeln!(f, "{} {}\n", loc, self.message.bright_white())?; + writeln!( + f, + "{}\n{} {}\n", + self.message.bright_white(), + loc, + *self.at.file, + )?; writeln!(f, " {}", self.source)?; - // The presence of tabs makes presenting error indicators purely based - // on column position impossible, so here we iterrate over the existing - // string and mask out the non whitespace text inserting the error - // indicators and preserving any tab/space mixture. - let carat_line: String = self - .source - .chars() - .enumerate() - .map(|(i, x)| { - if i == self.at.col { - return '^'; - } - if x.is_whitespace() { - x - } else { - ' ' - } - }) - .collect(); + let carat_line = carat_line(&self.source, &self.at); write!(f, " {}", carat_line.bright_red()) } } @@ -105,6 +82,9 @@ pub struct TokenError { /// The source line the token error occured on. pub source: String, + + /// The soruce file where the token error was encountered. + pub file: Arc, } impl fmt::Display for TokenError { @@ -112,28 +92,22 @@ impl fmt::Display for TokenError { let loc = format!("[{}:{}]", self.line + 1, self.col + 1) .as_str() .bright_red(); - writeln!(f, "{} {}\n", loc, "unrecognized token".bright_white())?; + writeln!( + f, + "{}\n{} {}\n", + "unrecognized token".bright_white(), + loc, + *self.file, + )?; writeln!(f, " {}", self.source)?; - // The presence of tabs makes presenting error indicators purely based - // on column position impossible, so here we iterrate over the existing - // string and mask out the non whitespace text inserting the error - // indicators and preserving any tab/space mixture. - let carat_line: String = self - .source - .chars() - .enumerate() - .map(|(i, x)| { - if i >= self.col && i < self.col + self.len { - return '^'; - } - if x.is_whitespace() { - x - } else { - ' ' - } - }) - .collect(); + let at = Token { + kind: Kind::Eof, + line: self.line, + col: self.col, + file: Arc::new(self.source.clone()), + }; + let carat_line = carat_line(&self.source, &at); write!(f, " {}", carat_line.bright_red()) } } @@ -153,9 +127,11 @@ impl fmt::Display for Error { Self::Lexer(e) => e.fmt(f), Self::Parser(e) => e.fmt(f), Self::Semantic(errors) => { - for e in errors { + for e in &errors[..errors.len() - 1] { e.fmt(f)?; + writeln!(f)?; } + errors[errors.len() - 1].fmt(f)?; Ok(()) } } @@ -192,6 +168,9 @@ pub struct PreprocessorError { /// The source line the token error occured on. pub source: String, + + /// The soruce file where the token error was encountered. + pub file: Arc, } impl fmt::Display for PreprocessorError { @@ -199,13 +178,36 @@ impl fmt::Display for PreprocessorError { let loc = format!("[{}]", self.line + 1).as_str().bright_red(); writeln!( f, - "{} {}: {}\n", - loc, - "preporcessor error".bright_white(), + "{}\n{} {}\n", self.message.bright_white(), + loc, + *self.file, )?; writeln!(f, " {}", self.source) } } impl std::error::Error for PreprocessorError {} + +fn carat_line(line: &str, at: &Token) -> String { + // The presence of tabs makes presenting error indicators purely based + // on column position impossible, so here we iterrate over the existing + // string and mask out the non whitespace text inserting the error + // indicators and preserving any tab/space mixture. + let mut carat_line = String::new(); + for x in line[..at.col].chars() { + if x.is_whitespace() { + carat_line.push(x); + } else { + carat_line.push(' '); + } + } + for x in line[at.col..].chars() { + if x.is_whitespace() || Lexer::is_separator(x) { + break; + } else { + carat_line.push('^'); + } + } + carat_line +} diff --git a/p4/src/lexer.rs b/p4/src/lexer.rs index e0f8124f..ade2c744 100644 --- a/p4/src/lexer.rs +++ b/p4/src/lexer.rs @@ -1,5 +1,6 @@ use crate::error::TokenError; use std::fmt; +use std::sync::Arc; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum Kind { @@ -243,6 +244,9 @@ pub struct Token { /// Column number of the first character in this token. pub col: usize, + + /// The file this token came from. + pub file: Arc, } impl fmt::Display for Token { @@ -252,17 +256,17 @@ impl fmt::Display for Token { } pub struct Lexer<'a> { - //pub input: &'a str, pub line: usize, pub col: usize, pub show_tokens: bool, pub(crate) lines: Vec<&'a str>, cursor: &'a str, + file: Arc, } impl<'a> Lexer<'a> { - pub fn new(lines: Vec<&'a str>) -> Self { + pub fn new(lines: Vec<&'a str>, filename: Arc) -> Self { if lines.is_empty() { return Self { cursor: "", @@ -270,6 +274,7 @@ impl<'a> Lexer<'a> { col: 0, lines, show_tokens: false, + file: filename, }; } @@ -281,6 +286,7 @@ impl<'a> Lexer<'a> { col: 0, lines, show_tokens: false, + file: filename, } } @@ -300,6 +306,7 @@ impl<'a> Lexer<'a> { kind: Kind::Eof, col: self.col, line: self.line, + file: self.file.clone(), }); } @@ -310,6 +317,7 @@ impl<'a> Lexer<'a> { kind: Kind::Eof, col: self.col, line: self.line, + file: self.file.clone(), }); } self.skip_whitespace(); @@ -666,6 +674,7 @@ impl<'a> Lexer<'a> { line: self.line, col: self.col - len, source: self.lines[self.line].into(), + file: self.file.clone(), len, }) } @@ -689,6 +698,7 @@ impl<'a> Lexer<'a> { kind: Kind::Identifier(tok.into()), col: self.col, line: self.line, + file: self.file.clone(), }; self.col += len; self.cursor = &self.cursor[len..]; @@ -724,6 +734,7 @@ impl<'a> Lexer<'a> { kind: ctor(bits, value), col: self.col, line: self.line, + file: self.file.clone(), }; Some(token) } @@ -755,6 +766,7 @@ impl<'a> Lexer<'a> { kind: ctor(bits, value), col: self.col, line: self.line, + file: self.file.clone(), }; Some(token) } @@ -833,6 +845,7 @@ impl<'a> Lexer<'a> { kind: Kind::IntLiteral(value), col: self.col, line: self.line, + file: self.file.clone(), }; self.col += len; self.cursor = &self.cursor[len..]; @@ -949,6 +962,7 @@ impl<'a> Lexer<'a> { kind, col: self.col, line: self.line, + file: self.file.clone(), }; self.col += len; self.cursor = &self.cursor[len..]; @@ -1018,7 +1032,7 @@ impl<'a> Lexer<'a> { &self.cursor[..len] } - fn is_separator(c: char) -> bool { + pub(crate) fn is_separator(c: char) -> bool { if c.is_whitespace() { return true; } diff --git a/p4/src/parser.rs b/p4/src/parser.rs index 5b48ff74..995149e7 100644 --- a/p4/src/parser.rs +++ b/p4/src/parser.rs @@ -24,10 +24,10 @@ impl<'a> Parser<'a> { } } - pub fn run(&mut self) -> Result { + pub fn run(&mut self, ast: &mut AST) -> Result<(), Error> { let mut gp = GlobalParser::new(self); - let ast = gp.run()?; - Ok(ast) + gp.run(ast)?; + Ok(()) } pub fn next_token(&mut self) -> Result { @@ -622,22 +622,20 @@ impl<'a, 'b> GlobalParser<'a, 'b> { Self { parser } } - pub fn run(&'b mut self) -> Result { - let mut prog = AST::default(); - + pub fn run(&'b mut self, ast: &mut AST) -> Result<(), Error> { loop { match self.parser.next_token() { Ok(token) => { if token.kind == lexer::Kind::Eof { break; } - self.handle_token(token, &mut prog)?; + self.handle_token(token, ast)?; } Err(e) => return Err(e), }; } - Ok(prog) + Ok(()) } pub fn handle_token( diff --git a/p4/src/preprocessor.rs b/p4/src/preprocessor.rs index c88e089b..622fc807 100644 --- a/p4/src/preprocessor.rs +++ b/p4/src/preprocessor.rs @@ -1,5 +1,6 @@ use crate::error::PreprocessorError; use std::fmt::Write; +use std::sync::Arc; #[derive(Clone, Debug)] struct Macro { @@ -18,9 +19,11 @@ pub struct PreprocessorElements { pub includes: Vec, } -pub fn run(source: &str) -> Result { +pub fn run( + source: &str, + filename: Arc, +) -> Result { let mut result = PreprocessorResult::default(); - //let mut macros = Vec::new(); let mut macros_to_process = Vec::new(); let mut current_macro: Option = None; @@ -44,7 +47,6 @@ pub fn run(source: &str) -> Result { None => {} Some(ref mut m) => { if !line.ends_with('\\') { - //m.body += &format!("\n{}", line); write!(m.body, "\n{}", line).unwrap(); macros_to_process.push(m.clone()); current_macro = None; @@ -60,7 +62,7 @@ pub fn run(source: &str) -> Result { // if line.starts_with("#include") { - process_include(i, line, &mut result)?; + process_include(i, line, &mut result, &filename)?; continue; } @@ -69,7 +71,7 @@ pub fn run(source: &str) -> Result { // if line.starts_with("#define") { - let (name, value) = process_macro_begin(i, line)?; + let (name, value) = process_macro_begin(i, line, &filename)?; let m = Macro { name, body: value }; if !line.ends_with('\\') { macros_to_process.push(m.clone()); @@ -107,6 +109,7 @@ fn process_include( i: usize, line: &str, result: &mut PreprocessorResult, + filename: &Arc, ) -> Result<(), PreprocessorError> { let (begin, end) = if let Some(begin) = line.find('<') { match line[begin..].find('>') { @@ -116,6 +119,7 @@ fn process_include( line: i, message: "Unterminated '<'".into(), source: line.to_string(), + file: filename.clone(), }) } } @@ -127,6 +131,7 @@ fn process_include( line: i, message: "Unterminated '\"'".into(), source: line.to_string(), + file: filename.clone(), }) } } @@ -135,6 +140,7 @@ fn process_include( line: i, message: "Invalid #include".into(), source: line.to_string(), + file: filename.clone(), }); }; @@ -148,6 +154,7 @@ fn process_include( c, ), source: line.to_string(), + file: filename.clone(), }); } } @@ -160,6 +167,7 @@ fn process_include( fn process_macro_begin( i: usize, line: &str, + filename: &Arc, ) -> Result<(String, String), PreprocessorError> { let mut parts = line.split_whitespace(); // discard #define @@ -172,6 +180,7 @@ fn process_macro_begin( line: i, message: "Macros must have a name".into(), source: line.to_string(), + file: filename.clone(), }) } }; diff --git a/test/src/p4/core.p4 b/test/src/p4/core.p4 new file mode 100644 index 00000000..8fcc8ebe --- /dev/null +++ b/test/src/p4/core.p4 @@ -0,0 +1,11 @@ +extern packet_in { + void extract(out T headerLvalue); + void extract(out T variableSizeHeader, in bit<32> varFieldSizeBits); + T lookahead(); + bit<32> length(); // This method may be unavailable in some architectures + void advance(bit<32> bits); +} + +extern packet_out { + void emit(in T hdr); +} diff --git a/test/src/p4/dynamic_router.p4 b/test/src/p4/dynamic_router.p4 index 9e1ee779..2c1fb99c 100644 --- a/test/src/p4/dynamic_router.p4 +++ b/test/src/p4/dynamic_router.p4 @@ -1,28 +1,6 @@ -// XXX import from core.p4 -extern packet_in { - void extract(out T headerLvalue); - void extract(out T variableSizeHeader, in bit<32> varFieldSizeBits); - T lookahead(); - bit<32> length(); // This method may be unavailable in some architectures - void advance(bit<32> bits); -} - -// XXX import from core.p4 -extern packet_out { - void emit(in T hdr); -} - -// XXX import from softnpu.p4 -struct IngressMetadata { - bit<8> port; - bool nat; - bit<16> l4_dst_port; -} -struct EgressMetadata { - bit<8> port; - bit<128> nexthop; - bool drop; -} +#include +#include +#include SoftNPU( parse(), @@ -30,34 +8,9 @@ SoftNPU( ) main; struct headers_t { - ethernet_t ethernet; - sidecar_t sidecar; - ipv6_t ipv6; -} - -header sidecar_t { - bit<8> sc_code; - bit<8> sc_ingress; - bit<8> sc_egress; - bit<16> sc_ether_type; - bit<128> sc_payload; -} - -header ethernet_t { - bit<48> dst; - bit<48> src; - bit<16> ether_type; -} - -header ipv6_t { - bit<4> version; - bit<8> traffic_class; - bit<20> flow_label; - bit<16> payload_len; - bit<8> next_hdr; - bit<8> hop_limit; - bit<128> src; - bit<128> dst; + ethernet_h ethernet; + sidecar_h sidecar; + ipv6_h ipv6; } parser parse( diff --git a/test/src/p4/dynamic_router_noaddr.p4 b/test/src/p4/dynamic_router_noaddr.p4 index a484aebb..88d8e8eb 100644 --- a/test/src/p4/dynamic_router_noaddr.p4 +++ b/test/src/p4/dynamic_router_noaddr.p4 @@ -1,26 +1,6 @@ -// XXX import from core.p4 -extern packet_in { - void extract(out T headerLvalue); - void extract(out T variableSizeHeader, in bit<32> varFieldSizeBits); - T lookahead(); - bit<32> length(); // This method may be unavailable in some architectures - void advance(bit<32> bits); -} - -// XXX import from core.p4 -extern packet_out { - void emit(in T hdr); -} - -// XXX import from softnpu.p4 -struct IngressMetadata { - bit<8> port; - bool nat; - bit<16> l4_dst_port; -} -struct EgressMetadata { - bit<8> port; -} +#include +#include +#include SoftNPU( parse(), @@ -28,34 +8,9 @@ SoftNPU( ) main; struct headers_t { - ethernet_t ethernet; - sidecar_t sidecar; - ipv6_t ipv6; -} - -header sidecar_t { - bit<8> sc_code; - bit<8> sc_ingress; - bit<8> sc_egress; - bit<16> sc_ether_type; - bit<128> sc_payload; -} - -header ethernet_t { - bit<48> dst; - bit<48> src; - bit<16> ether_type; -} - -header ipv6_t { - bit<4> version; - bit<8> traffic_class; - bit<20> flow_label; - bit<16> payload_len; - bit<8> next_hdr; - bit<8> hop_limit; - bit<128> src; - bit<128> dst; + ethernet_h ethernet; + sidecar_h sidecar; + ipv6_h ipv6; } parser parse( diff --git a/test/src/p4/dynamic_router_noaddr_nbr.p4 b/test/src/p4/dynamic_router_noaddr_nbr.p4 index 2edeeb6b..1e38afaa 100644 --- a/test/src/p4/dynamic_router_noaddr_nbr.p4 +++ b/test/src/p4/dynamic_router_noaddr_nbr.p4 @@ -1,28 +1,6 @@ -// XXX import from core.p4 -extern packet_in { - void extract(out T headerLvalue); - void extract(out T variableSizeHeader, in bit<32> varFieldSizeBits); - T lookahead(); - bit<32> length(); // This method may be unavailable in some architectures - void advance(bit<32> bits); -} - -// XXX import from core.p4 -extern packet_out { - void emit(in T hdr); -} - -// XXX import from softnpu.p4 -struct IngressMetadata { - bit<8> port; - bool nat; - bit<16> l4_dst_port; -} -struct EgressMetadata { - bit<8> port; - bit<128> nexthop; - bool drop; -} +#include +#include +#include SoftNPU( parse(), @@ -30,34 +8,9 @@ SoftNPU( ) main; struct headers_t { - ethernet_t ethernet; - sidecar_t sidecar; - ipv6_t ipv6; -} - -header sidecar_t { - bit<8> sc_code; - bit<8> sc_ingress; - bit<8> sc_egress; - bit<16> sc_ether_type; - bit<128> sc_payload; -} - -header ethernet_t { - bit<48> dst; - bit<48> src; - bit<16> ether_type; -} - -header ipv6_t { - bit<4> version; - bit<8> traffic_class; - bit<20> flow_label; - bit<16> payload_len; - bit<8> next_hdr; - bit<8> hop_limit; - bit<128> src; - bit<128> dst; + ethernet_h ethernet; + sidecar_h sidecar; + ipv6_h ipv6; } parser parse( diff --git a/test/src/p4/headers.p4 b/test/src/p4/headers.p4 new file mode 100644 index 00000000..64036644 --- /dev/null +++ b/test/src/p4/headers.p4 @@ -0,0 +1,77 @@ +header sidecar_h { + bit<8> sc_code; + bit<8> sc_ingress; + bit<8> sc_egress; + bit<16> sc_ether_type; + bit<128> sc_payload; +} + +header ethernet_h { + bit<48> dst; + bit<48> src; + bit<16> ether_type; +} + +header ipv6_h { + bit<4> version; + bit<8> traffic_class; + bit<20> flow_label; + bit<16> payload_len; + bit<8> next_hdr; + bit<8> hop_limit; + bit<128> src; + bit<128> dst; +} + +header ipv4_h { + bit<4> version; + bit<4> ihl; + bit<8> diffserv; + bit<16> total_len; + bit<16> identification; + bit<3> flags; + bit<13> frag_offset; + bit<8> ttl; + bit<8> protocol; + bit<16> hdr_checksum; + bit<32> src; + bit<32> dst; +} + +header udp_h { + bit<16> src_port; + bit<16> dst_port; + bit<16> len; + bit<16> checksum; +} + +header tcp_h { + bit<16> src_port; + bit<16> dst_port; + bit<32> seq_no; + bit<32> ack_no; + bit<4> data_offset; + bit<4> res; + bit<8> flags; + bit<16> window; + bit<16> checksum; + bit<16> urgent_ptr; +} + +header icmp_h { + bit<8> type; + bit<8> code; + bit<16> hdr_checksum; + bit<32> data; +} + +header geneve_h { + bit<2> version; + bit<6> opt_len; + bit<1> ctrl; + bit<1> crit; + bit<6> reserved; + bit<16> protocol; + bit<24> vni; + bit<8> reserved2; +} diff --git a/test/src/p4/sidecar-lite.p4 b/test/src/p4/sidecar-lite.p4 index 1dc55042..e058037f 100644 --- a/test/src/p4/sidecar-lite.p4 +++ b/test/src/p4/sidecar-lite.p4 @@ -1,35 +1,6 @@ -// XXX import from core.p4 -extern packet_in { - void extract(out T headerLvalue); - void extract(out T variableSizeHeader, in bit<32> varFieldSizeBits); - T lookahead(); - bit<32> length(); // This method may be unavailable in some architectures - void advance(bit<32> bits); -} - -// XXX import from core.p4 -extern packet_out { - void emit(in T hdr); -} - -// XXX import from softnpu.p4 -struct IngressMetadata { - bit<8> port; - bool nat; - bit<16> nat_id; -} - -// XXX import from softnpu.p4 -struct EgressMetadata { - bit<8> port; - bit<128> nexthop; - bool drop; -} - -// XXX import from softnpu.p4 -extern Checksum { - bit<16> run(in T data); -} +#include +#include +#include SoftNPU( parse(), @@ -53,84 +24,6 @@ struct headers_t { udp_h inner_udp; } -header sidecar_h { - bit<8> sc_code; - bit<8> sc_ingress; - bit<8> sc_egress; - bit<16> sc_ether_type; - bit<128> sc_payload; -} - -header ethernet_h { - bit<48> dst; - bit<48> src; - bit<16> ether_type; -} - -header ipv6_h { - bit<4> version; - bit<8> traffic_class; - bit<20> flow_label; - bit<16> payload_len; - bit<8> next_hdr; - bit<8> hop_limit; - bit<128> src; - bit<128> dst; -} - -header ipv4_h { - bit<4> version; - bit<4> ihl; - bit<8> diffserv; - bit<16> total_len; - bit<16> identification; - bit<3> flags; - bit<13> frag_offset; - bit<8> ttl; - bit<8> protocol; - bit<16> hdr_checksum; - bit<32> src; - bit<32> dst; -} - -header udp_h { - bit<16> src_port; - bit<16> dst_port; - bit<16> len; - bit<16> checksum; -} - -header tcp_h { - bit<16> src_port; - bit<16> dst_port; - bit<32> seq_no; - bit<32> ack_no; - bit<4> data_offset; - bit<4> res; - bit<8> flags; - bit<16> window; - bit<16> checksum; - bit<16> urgent_ptr; -} - -header icmp_h { - bit<8> type; - bit<8> code; - bit<16> hdr_checksum; - bit<32> data; -} - -header geneve_h { - bit<2> version; - bit<6> opt_len; - bit<1> ctrl; - bit<1> crit; - bit<6> reserved; - bit<16> protocol; - bit<24> vni; - bit<8> reserved2; -} - parser parse( packet_in pkt, out headers_t hdr, diff --git a/test/src/p4/softnpu.p4 b/test/src/p4/softnpu.p4 new file mode 100644 index 00000000..5b3c8895 --- /dev/null +++ b/test/src/p4/softnpu.p4 @@ -0,0 +1,15 @@ +struct IngressMetadata { + bit<8> port; + bool nat; + bit<16> nat_id; +} + +struct EgressMetadata { + bit<8> port; + bit<128> nexthop; + bool drop; +} + +extern Checksum { + bit<16> run(in T data); +} diff --git a/x4c/src/bin/x4c.rs b/x4c/src/bin/x4c.rs new file mode 100644 index 00000000..365d9e34 --- /dev/null +++ b/x4c/src/bin/x4c.rs @@ -0,0 +1,45 @@ +use anyhow::Result; +use clap::Parser; +use p4::ast::AST; +use std::sync::Arc; + +fn main() { + if let Err(e) = run() { + println!("{}", e); + std::process::exit(1); + } +} + +fn run() -> Result<()> { + let opts = x4c::Opts::parse(); + let filename = Arc::new(opts.filename.clone()); + let mut ast = AST::default(); + x4c::process_file(filename, &mut ast, &opts)?; + let (hlir, _) = p4::check::all(&ast); + + if opts.check { + return Ok(()); + } + + match opts.target { + x4c::Target::Rust => { + p4_rust::sanitize(&mut ast); + p4_rust::emit( + &ast, + &hlir, + &opts.out, + p4_rust::Settings { + pipeline_name: "main".to_owned(), + }, + )?; + } + x4c::Target::RedHawk => { + todo!("RedHawk code generator"); + } + x4c::Target::Docs => { + todo!("Docs code generator"); + } + } + + Ok(()) +} diff --git a/x4c/src/main.rs b/x4c/src/lib.rs similarity index 57% rename from x4c/src/main.rs rename to x4c/src/lib.rs index edc19143..27ca8e9d 100644 --- a/x4c/src/main.rs +++ b/x4c/src/lib.rs @@ -1,105 +1,89 @@ use anyhow::{anyhow, Result}; use clap::Parser; use p4::check::Diagnostics; -use p4::{check, error, error::SemanticError, lexer, parser, preprocessor}; +use p4::{ + ast::AST, check, error, error::SemanticError, lexer, parser, preprocessor, +}; use std::fs; +use std::sync::Arc; #[derive(Parser)] #[clap(version = "0.1")] -struct Opts { +pub struct Opts { /// Show parsed lexical tokens. #[clap(long)] - show_tokens: bool, + pub show_tokens: bool, /// Show parsed abstract syntax tree. #[clap(long)] - show_ast: bool, + pub show_ast: bool, /// Show parsed preprocessor info. #[clap(long)] - show_pre: bool, + pub show_pre: bool, /// Show high-level intermediate representation info. #[clap(long)] - show_hlir: bool, + pub show_hlir: bool, /// File to compile. - filename: String, + pub filename: String, /// What target to generate code for. #[clap(arg_enum, default_value_t = Target::Rust)] - target: Target, + pub target: Target, /// Just check code, do not compile. #[clap(long)] - check: bool, + pub check: bool, /// Filename to write generated code to. #[clap(short, long, default_value = "out.rs")] - out: String, + pub out: String, } #[derive(clap::ArgEnum, Clone)] -enum Target { +pub enum Target { Rust, RedHawk, Docs, } -fn main() -> Result<()> { - let opts: Opts = Opts::parse(); +pub fn process_file( + filename: Arc, + ast: &mut AST, + opts: &Opts, +) -> Result<()> { + let contents = fs::read_to_string(&*filename) + .map_err(|e| anyhow!("read input: {}: {}", &*filename, e))?; - let contents = fs::read_to_string(opts.filename) - .map_err(|e| anyhow!("read input: {}", e))?; - - let ppr = preprocessor::run(&contents)?; + let ppr = preprocessor::run(&contents, filename.clone())?; if opts.show_pre { println!("{:#?}", ppr.elements); } + for included in &ppr.elements.includes { + process_file(Arc::new(included.clone()), ast, opts)? + } + let lines: Vec<&str> = ppr.lines.iter().map(|x| x.as_str()).collect(); - //println!("lines\n{:#?}", lines); - let mut lxr = lexer::Lexer::new(lines.clone()); + let mut lxr = lexer::Lexer::new(lines.clone(), filename); lxr.show_tokens = opts.show_tokens; let mut psr = parser::Parser::new(lxr); - let mut ast = psr.run()?; + psr.run(ast)?; if opts.show_ast { println!("{:#?}", ast); } - let (hlir, diags) = check::all(&ast); + let (hlir, diags) = check::all(ast); check(&lines, &diags)?; if opts.show_hlir { println!("{:#?}", hlir); } - if opts.check { - return Ok(()); - } - - match opts.target { - Target::Rust => { - p4_rust::sanitize(&mut ast); - p4_rust::emit( - &ast, - &hlir, - &opts.out, - p4_rust::Settings { - pipeline_name: "main".to_owned(), - }, - )?; - } - Target::RedHawk => { - todo!("RedHawk code generator"); - } - Target::Docs => { - todo!("Docs code generator"); - } - } - Ok(()) }