Skip to content

Commit

Permalink
allow optional thousands separators (eg. 1_000) for TimeMsg
Browse files Browse the repository at this point in the history
  • Loading branch information
karnpapon committed Dec 24, 2024
1 parent fd54a56 commit b900d4c
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 23 deletions.
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,28 @@ It has two simple features:
- eg. `65.4321_f64` is equivalent to `65.4321 as f64` (`Explicit conversion`)
- it will be parsed osc as `Double(65.4321)`, otherwise `osc` will parsed it based on the input (eg. `65.4321` = `f32`).
- see supported types below.
- complete osc example `/s_new "default with whitespace" 1002 'A' 12_i32 12_i64 -12 -12_i32 -12_i64 12.4533 1.234_f64 #2f14DF12 ~00110011 @2208988800.23 [12,20,true] %[10,20,30]`
- complete osc example `/s_new "default with whitespace" 1002 'A' 12_i32 12_i64 -12 -12_i32 -12_i64 12.4533 1.234_f64 #2f14DF12 ~00110011 @2_208_988_800.23 [12,20,true] %[10,20,30]`

## Types

`oscd` follows [OscType](https://docs.rs/rosc/latest/rosc/enum.OscType.html) from [rosc](https://github.com/klingtnet/rosc) library

| status | types | example | notes |
| ------- | -------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ☑ | Int(i32) | `1234` or `1234_i32` | |
| ☑ | Long(i64) | `1234_i64` | |
| ☑ | Float(f32) | `1234.32` or `1234.32_f32` | |
| ☑ | Double(f64) | `1234.25434_f64` | |
| ☑ | String(String) | `"str goes here"` | wrapped in doulble quotes is needed |
| ☑ | Bool(bool) | `true` or `false` | |
| ☑ | Char(char) | `'S'` | wrapped in single quote is needed |
| ☑ | Blob(Vec<u8>) | `%[10,20,30]` | prefix with `%` separated by `,` |
| &#9745; | Time(OscTime) | `@2208988800.20` | prefix with `@` separate fractional by `.` eg. `@<seconds>.<fractional>`, A time tag in OSC message consists of two 32-bit integers where the first one denotes the number of seconds since 1900-01-01 ([RFC 5905](https://datatracker.ietf.org/doc/html/rfc5905)) and the second the fractions of a second |
| &#9745; | Color(OscColor) | `#2F14DF2A` | prefix with `#` followed by base16 `#<red><green><blue><alpha>` |
| &#9745; | Midi(OscMidiMessage) | `~01F14FA4` | prefix with `~` followed by base16 `~<port><status><data1><data2>` |
| &#9745; | Array(OscArray) | `[10,20,true]` | |
| &#9745; | Nil | `Nil` | |
| &#9745; | Inf | `Inf` | |
| status | types | example | notes |
| ------- | -------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| &#9745; | Int(i32) | `1234` or `1234_i32` | |
| &#9745; | Long(i64) | `1234_i64` | |
| &#9745; | Float(f32) | `1234.32` or `1234.32_f32` | |
| &#9745; | Double(f64) | `1234.25434_f64` | |
| &#9745; | String(String) | `"str goes here"` | wrapped in doulble quotes is needed |
| &#9745; | Bool(bool) | `true` or `false` | |
| &#9745; | Char(char) | `'S'` | wrapped in single quote is needed |
| &#9745; | Blob(Vec&#60;u8>) | `%[10,20,30]` | prefix with `%` separated by `,` |
| &#9745; | Time(OscTime) | `@2208988800.20` a "thousand separator" is also allowed eg. `@2_208_988_800.20` | prefix with `@` separate fractional by `.` eg. `@<seconds>.<fractional>`, A time tag in OSC message consists of two 32-bit integers where the first one denotes the number of seconds since 1900-01-01 ([RFC 5905](https://datatracker.ietf.org/doc/html/rfc5905)) and the second the fractions of a second |
| &#9745; | Color(OscColor) | `#2F14DF2A` | prefix with `#` followed by base16 `#<red><green><blue><alpha>` |
| &#9745; | Midi(OscMidiMessage) | `~01F14FA4` | prefix with `~` followed by base16 `~<port><status><data1><data2>` |
| &#9745; | Array(OscArray) | `[10,20,true]` | |
| &#9745; | Nil | `Nil` | |
| &#9745; | Inf | `Inf` | |

## Development

Expand Down
50 changes: 44 additions & 6 deletions src/analyser/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use std::ops::Range;
use std::{slice, u8};
use std::{str, vec};

use bytes::complete::take_while;
use bytes::complete::{is_a, take_while};
use combinator::map_res;
use nom::branch::alt;
use nom::bytes::complete::{tag, take, take_till1, take_while_m_n};
use nom::character::complete::{
Expand Down Expand Up @@ -462,13 +463,50 @@ pub fn lex_midimsg(input: LocatedSpan) -> IResult<Token> {

// --------- TimeMsg ---------

// TODO: support "thousand-separator" eg. 10_000_000
fn parse_digits_with_underscores(input: LocatedSpan) -> IResult<String> {
let parser = many0(alt((
map(digit1, |s: LocatedSpan| s.to_string()),
map(char1('_'), |_| "_".to_string()),
)));

map(parser, |parts| parts.concat().replace('_', ""))(input)
}

fn parse_seconds(input: LocatedSpan) -> IResult<u32> {
let (input, seconds_str) = parse_digits_with_underscores(input)?;
let seconds = seconds_str.parse::<u32>().unwrap_or(0);
Ok((input, seconds))
}

fn parse_fractional(input: LocatedSpan) -> IResult<u32> {
let (input, fractional_str) =
opt(terminated(parse_digits_with_underscores, many0(char1(' '))))(input)?;

let fractional = fractional_str.unwrap_or_else(|| "".to_string());
let fractional = if !fractional.is_empty() {
fractional.parse::<u32>().unwrap_or(0)
} else {
0
};

Ok((input, fractional))
}

fn parse_time_segment(input: LocatedSpan) -> IResult<(u32, u32)> {
let mut parser = alt((map(
tuple((parse_seconds, char1('.'), parse_fractional)),
|(seconds, _, fractional): (u32, char, u32)| (seconds, fractional),
),));

parser(input)
}

pub fn lex_timemsg(input: LocatedSpan) -> IResult<Token> {
let (inp, _) = tag("@")(input)?;
let (remaining, (seconds, fractional)) = separated_pair(digit1, char1('.'), digit1)(inp)?;
let (inp, _) = tag("@")(input.clone())?;
let (remaining, (seconds, fractional)) = parse_time_segment(inp)?;
let msg = TimeMsg {
seconds: seconds.parse::<u32>().unwrap(),
fractional: fractional.parse::<u32>().unwrap(),
seconds,
fractional,
};
Ok((remaining, Token::TimeMsg(msg)))
}
Expand Down

0 comments on commit b900d4c

Please sign in to comment.