Skip to content

Commit

Permalink
add support for AS numbers as literals and in the type system
Browse files Browse the repository at this point in the history
  • Loading branch information
tertsdiepraam committed Sep 9, 2024
1 parent 31002e7 commit 7d86ab5
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 21 deletions.
71 changes: 62 additions & 9 deletions src/ast.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
//! Abstract Syntax Tree (AST) for Roto
//!
//! A [`SyntaxTree`] is the output of the Roto parser. It contains a
//! representation of the Roto script as Rust types for further processing.
use inetnum::asn::Asn;
use string_interner::symbol::SymbolU32;

use crate::parser::meta::Meta;
Expand Down Expand Up @@ -47,6 +53,7 @@ pub struct FilterMapBody {
pub apply: Meta<Block>,
}

/// A function declaration, including the [`Block`] forming its definition
#[derive(Clone, Debug)]
pub struct FunctionDeclaration {
pub ident: Meta<Identifier>,
Expand All @@ -55,35 +62,67 @@ pub struct FunctionDeclaration {
pub body: Meta<Block>,
}

/// A block of multiple Roto expressions
#[derive(Clone, Debug)]
pub struct Block {
pub exprs: Vec<Meta<Expr>>,
pub last: Option<Box<Meta<Expr>>>,
}

/// A Roto expression
#[derive(Clone, Debug)]
pub enum Expr {
/// Return from the current function or filter-map
///
/// Optionally takes an expression for the value being returned.
Return(ReturnKind, Option<Box<Meta<Expr>>>),
/// a literal, or a chain of field accesses and/or methods on a literal,
/// e.g. `10.0.0.0/8.covers(..)`

/// A literal expression
Literal(Meta<Literal>),

/// A match expression,
Match(Box<Meta<Match>>),

/// A function call expression
FunctionCall(Meta<Identifier>, Meta<Vec<Meta<Expr>>>),

/// A method call expression
///
/// Takes the expression of the _receiver_ (the expression that the
/// method is called on), the name of the method and a [`Vec`] of
/// arguments.
MethodCall(Box<Meta<Expr>>, Meta<Identifier>, Meta<Vec<Meta<Expr>>>),

/// A field access expression
Access(Box<Meta<Expr>>, Meta<Identifier>),

/// A variable use
Var(Meta<Identifier>),
/// a record that doesn't have a type mentioned in the assignment of it,
/// e.g `{ value_1: 100, value_2: "bla" }`. This can also be a sub-record
/// of a record that does have an explicit type.

/// A record that doesn't have a type mentioned in the assignment of it
///
/// For example: `{ value_1: 100, value_2: "bla" }`. This can also be a
/// sub-record of a record that does have an explicit type.
Record(Meta<Record>),
/// an expression of a record that does have a type, e.g. `MyType {
/// value_1: 100, value_2: "bla" }`, where MyType is a user-defined Record
/// Type.

/// An expression of a record that does have a type
///
/// For example: `MyType { value_1: 100, value_2: "bla" }`, where `MyType`
/// is a user-defined Record Type.
TypedRecord(Meta<Identifier>, Meta<Record>),

/// An expression that yields a list of values, e.g. `[100, 200, 300]`
List(Vec<Meta<Expr>>),

/// A unary not expression
Not(Box<Meta<Expr>>),

/// A binary operator expression
///
/// Takes a left operand, the operator and the right operand
BinOp(Box<Meta<Expr>>, BinOp, Box<Meta<Expr>>),

/// An if or if-else expression
IfElse(Box<Meta<Expr>>, Meta<Block>, Option<Meta<Block>>),
}

Expand Down Expand Up @@ -180,27 +219,41 @@ pub struct RecordType {
pub enum Literal {
#[allow(dead_code)]
String(String),
Asn(u32),
Asn(Asn),
IpAddress(IpAddress),
Integer(i64),
Bool(bool),
}

#[derive(Clone, Debug)]
pub enum BinOp {
/// Logical and (`&&`)
And,
/// Logical or (`||`)
Or,
/// Equals (`==`)
Eq,
/// Not equals (`!=`)
Ne,
/// Less than (`<`)
Lt,
/// Less than or equal (`<=`)
Le,
/// Greater than (`>`)
Gt,
/// Greater than or equal (`>=`)
Ge,
/// In
In,
/// Not in
NotIn,
/// Addition (`+`)
Add,
/// Subtraction (`-`)
Sub,
/// Multiplication (`*`)
Mul,
/// Division (`/`)
Div,
}

Expand Down
4 changes: 4 additions & 0 deletions src/codegen/check.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use inetnum::asn::Asn;

use crate::{
runtime::ty::{Reflect, TypeDescription, TypeRegistry},
typechecker::{
Expand Down Expand Up @@ -128,6 +130,7 @@ fn check_roto_type(
let I32: TypeId = TypeId::of::<i32>();
let I64: TypeId = TypeId::of::<i64>();
let UNIT: TypeId = TypeId::of::<()>();
let ASN: TypeId = TypeId::of::<Asn>();

let Some(rust_ty) = registry.get(rust_ty) else {
return false;
Expand All @@ -151,6 +154,7 @@ fn check_roto_type(
x if x == I32 => roto_ty == Type::Primitive(Primitive::I32),
x if x == I64 => roto_ty == Type::Primitive(Primitive::I64),
x if x == UNIT => roto_ty == Type::Primitive(Primitive::Unit),
x if x == ASN => roto_ty == Type::Primitive(Primitive::Asn),
_ => panic!(),
},
TypeDescription::ConstPtr(_) => todo!(),
Expand Down
3 changes: 2 additions & 1 deletion src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ impl ModuleBuilder {
match ty {
IrType::Bool | IrType::U8 | IrType::I8 => I8,
IrType::U16 | IrType::I16 => I16,
IrType::U32 | IrType::I32 => I32,
IrType::U32 | IrType::I32 | IrType::Asn => I32,
IrType::U64 | IrType::I64 => I64,
IrType::IpAddr => I32,
IrType::Pointer | IrType::ExtPointer => self.isa.pointer_type(),
Expand Down Expand Up @@ -678,6 +678,7 @@ impl<'c> FuncGen<'c> {
IrValue::I16(x) => (I16, *x as i64),
IrValue::I32(x) => (I32, *x as i64),
IrValue::I64(x) => (I64, *x),
IrValue::Asn(x) => (I32, x.into_u32() as i64),
IrValue::Pointer(x) => (pointer_ty, *x as i64),
_ => todo!(),
};
Expand Down
31 changes: 31 additions & 0 deletions src/codegen/tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use inetnum::asn::Asn;

use crate::{
pipeline::{test_file, Compiled},
runtime::tests::routecore_runtime,
Expand Down Expand Up @@ -551,6 +553,35 @@ fn issue_54() {
.unwrap_err();
}

#[test]
fn asn() {
let s = "
filter-map main(x: Asn) {
apply {
if x == AS1000 {
accept x
} else {
reject x
}
}
}
";

let mut p = compile(s);
let f = p
.get_function::<(Asn,), Verdict<Asn, Asn>>("main")
.expect("No function found (or mismatched types)");

assert_eq!(
f.call((Asn::from_u32(1000),)),
Verdict::Accept(Asn::from_u32(1000))
);
assert_eq!(
f.call((Asn::from_u32(2000),)),
Verdict::Reject(Asn::from_u32(2000))
);
}

// #[test]
// fn bmp_message() {
// let s = "
Expand Down
11 changes: 10 additions & 1 deletion src/lower/label.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
//! Labels for basic blocks in the IR
//!
//! These labels are designed to be unique, but still have some structure.
//! Technically, a unique number for each label would suffice, but that would
//! make the generated code harder to debug. Therefore, the labels follow
//! a hierachical scheme, where labels have a name, a counter and optionally
//! a parent. When they are displayed, we join the linked list formed by the
//! parent labels as separated by `::`.
use crate::ast::Identifier;

/// A label for a basic block
///
/// Should not be constructed directly, but using a [`LabelStore`].
/// This should not be constructed directly, but only via a [`LabelStore`].
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Label {
pub identifier: Identifier,
Expand Down Expand Up @@ -66,6 +73,7 @@ impl LabelStore {
LabelRef(self.labels.len() - 1)
}

/// Increment the counter of the label by one and return a reference to it
pub fn next(&mut self, previous: LabelRef) -> LabelRef {
let previous = &self.labels[previous.0];
self.labels.push(Label {
Expand All @@ -75,6 +83,7 @@ impl LabelStore {
LabelRef(self.labels.len() - 1)
}

/// Get the [`Label`] corresponding to a [`LabelRef`]
pub fn get(&self, label: LabelRef) -> &Label {
&self.labels[label.0]
}
Expand Down
4 changes: 3 additions & 1 deletion src/lower/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@ impl<'r> Lowerer<'r> {
fn literal(&mut self, lit: &Meta<Literal>) -> Operand {
match &lit.node {
Literal::String(_) => todo!(),
Literal::Asn(n) => IrValue::U32(*n).into(),
Literal::Asn(n) => IrValue::Asn(*n).into(),
Literal::IpAddress(addr) => IrValue::IpAddr(match addr {
ast::IpAddress::Ipv4(x) => IpAddr::V4(*x),
ast::IpAddress::Ipv6(x) => IpAddr::V6(*x),
Expand Down Expand Up @@ -1022,6 +1022,7 @@ impl<'r> Lowerer<'r> {
Type::Primitive(Primitive::I16) => IrType::I16,
Type::Primitive(Primitive::I32) => IrType::I32,
Type::Primitive(Primitive::I64) => IrType::I64,
Type::Primitive(Primitive::Asn) => IrType::Asn,
Type::IntVar(_) => IrType::I32,
Type::BuiltIn(_, _) => IrType::ExtPointer,
x if self.is_reference_type(&x) => IrType::Pointer,
Expand All @@ -1037,6 +1038,7 @@ fn binop_to_cmp(op: &ast::BinOp, ty: &Type) -> Option<ir::IntCmp> {
| Primitive::U32
| Primitive::U16
| Primitive::U8
| Primitive::Asn
| Primitive::Bool => false,
Primitive::I64
| Primitive::I32
Expand Down
20 changes: 17 additions & 3 deletions src/lower/value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::any::Any;
use std::fmt::{Debug, Display};

use inetnum::asn::Asn;

use super::ir::Operand;

/// A Roto value with type information at runtime
Expand All @@ -18,6 +20,7 @@ pub enum IrValue {
I16(i16),
I32(i32),
I64(i64),
Asn(Asn),
IpAddr(std::net::IpAddr),
Pointer(usize),
ExtPointer(*mut ()),
Expand All @@ -36,6 +39,7 @@ pub enum IrType {
I16,
I32,
I64,
Asn,
IpAddr,
Pointer,
ExtPointer,
Expand All @@ -49,7 +53,7 @@ impl IrType {
match self {
Bool | U8 | I8 => 1,
U16 | I16 => 2,
U32 | I32 => 4,
U32 | I32 | Asn => 4,
U64 | I64 => 8,
IpAddr => 4,
Pointer | ExtValue | ExtPointer => (usize::BITS / 8) as usize,
Expand All @@ -74,6 +78,7 @@ impl Display for IrType {
I16 => "i16",
I32 => "i32",
I64 => "i64",
Asn => "Asn",
IpAddr => "IpAddr",
Pointer => "Pointer",
ExtValue => "ExtValue",
Expand All @@ -94,6 +99,7 @@ impl PartialEq for IrValue {
(I8(l), I8(r)) => l == r,
(I16(l), I16(r)) => l == r,
(I32(l), I32(r)) => l == r,
(Asn(l), Asn(r)) => l == r,
(ExtValue(_), ExtValue(_)) => false,
(ExtPointer(_), ExtPointer(_)) => false,
(Pointer(_), Pointer(_)) => panic!("can't compare pointers"),
Expand All @@ -117,6 +123,7 @@ impl IrValue {
I16(_) => IrType::I16,
I32(_) => IrType::I32,
I64(_) => IrType::I64,
Asn(_) => IrType::Asn,
IpAddr(_) => IrType::I32,
Pointer(_) => IrType::Pointer,
ExtValue(_) => IrType::ExtValue,
Expand All @@ -135,6 +142,7 @@ impl IrValue {
Self::I16(x) => x,
Self::I32(x) => x,
Self::I64(x) => x,
Self::Asn(x) => x,
Self::IpAddr(x) => x,
Self::Pointer(x) => x,
Self::ExtValue(x) => x,
Expand All @@ -161,8 +169,8 @@ impl IrValue {
IrValue::I32(*x)
} else if let Some(x) = any.downcast_ref() {
IrValue::I64(*x)
// } else if let Some(x) = any.downcast_ref() {
// todo!()
} else if let Some(x) = any.downcast_ref() {
IrValue::Asn(*x)
} else {
panic!("Could not downcast");
}
Expand All @@ -179,6 +187,7 @@ impl IrValue {
Self::I16(x) => x.to_ne_bytes().into(),
Self::I32(x) => x.to_ne_bytes().into(),
Self::I64(x) => x.to_ne_bytes().into(),
Self::Asn(x) => x.into_u32().to_ne_bytes().into(),
Self::IpAddr(_) => todo!(),
Self::Pointer(x) => x.to_ne_bytes().into(),
Self::ExtValue(x) => x.clone(),
Expand Down Expand Up @@ -230,6 +239,10 @@ impl IrValue {
let val: &[u8; 8] = val.try_into().unwrap();
Self::I64(i64::from_ne_bytes(*val))
}
IrType::Asn => {
let val: &[u8; 4] = val.try_into().unwrap();
Self::Asn(Asn::from_u32(u32::from_ne_bytes(*val)))
}
IrType::IpAddr => {
let val: &[u8; 32] = val.try_into().unwrap();
if val[0] == 0 {
Expand Down Expand Up @@ -312,6 +325,7 @@ impl Display for IrValue {
I16(x) => write!(f, "i16({x})"),
I32(x) => write!(f, "i32({x})"),
I64(x) => write!(f, "i64({x})"),
Asn(x) => write!(f, "Asn({x})"),
IpAddr(x) => write!(f, "IpAddr({x})"),
Pointer(x) => write!(f, "Pointer({x})"),
ExtValue(..) => write!(f, "ExtValue(..)"),
Expand Down
Loading

0 comments on commit 7d86ab5

Please sign in to comment.