honeycomb/
language.rs

1/// This module is useful for consuming common language
2/// tokens such as strings, identifiers, punctuation,
3/// floats, and arrays
4
5/// Import necessary atoms
6use crate::{
7    atoms::{if_take, list, none_of, one_of, opt, seq, seq_no_ws, space, sym},
8    transform::{collect, to_string},
9    Parser,
10};
11
12use alloc::borrow::ToOwned;
13use alloc::string::String;
14/// We need alloc!
15use alloc::vec::Vec;
16
17/// Consumes an alphabetic character
18pub fn alpha() -> Parser<char> {
19    if_take(|ch| ch.is_ascii_alphabetic())
20        % "a letter"
21}
22
23/// Consumes a numeric character
24pub fn numeral() -> Parser<char> {
25    if_take(|ch| ch.is_numeric())
26        % "a digit"
27}
28
29/// Consumes an alphanumeric character
30pub fn alphanumeric() -> Parser<char> {
31    alpha() | numeral()
32        % "an alphanumeric character"
33}
34
35/// Consumes a punctuation character
36/// One of ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
37pub fn punctuation() -> Parser<char> {
38    if_take(|ch| ch.is_ascii_punctuation())
39        % "one of ! \" # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \\ ] ^ _ ` { | } ~"
40}
41
42/// Consumes a common language token like
43/// A string
44/// A number
45/// An identifier
46/// Or Punctuation
47pub fn token() -> Parser<String> {
48    (space()
49        >> (string()       // Consume string
50        | number()     // Consume number
51        | identifier() // Consume identifier
52        | (punctuation() - to_string))
53        << space())
54        % "pne of string, number, identifier, or punctuation"
55}
56
57/// Consumes an alphanumeric identifier
58pub fn identifier() -> Parser<String> {
59    (alpha().is() >> (((alphanumeric() | sym('_')) * (1..31)) - collect))
60        % "an identifier"
61}
62
63/// Consumes a quoted string
64pub fn string() -> Parser<String> {
65    let special_char = sym('\\')
66        | sym('/')
67        | sym('"')
68        | sym('\'')
69        | (sym('b') - |_| '\x08')
70        | (sym('f') - |_| '\x0C')
71        | (sym('n') - |_| '\n')
72        | (sym('r') - |_| '\r')
73        | (sym('t') - |_| '\t');
74    let escape_sequence = sym('\\') >> special_char;
75
76    ((sym('"') >> ((none_of(b"\\\"") | escape_sequence).repeat(0..)) << sym('"'))
77        - |v| v.iter().collect::<String>())
78        % "a string"
79}
80
81/// Consumes a number
82pub fn number() -> Parser<String> {
83    let integer =
84        (one_of(b"0123456789").repeat(1..) - |cons| cons.iter().collect::<String>()) | seq("0");
85
86    let frac = sym('.') >> integer.clone();
87    let number = (space() >> opt(sym('-'))) & (space() >> integer) & (opt(frac) << space());
88
89    (number
90        - |v: ((Option<char>, String), Option<String>)| {
91            let mut result = String::new();
92
93            // Add the sign component
94            if let Some(ch) = (v.0).0 {
95                result.push(ch);
96            }
97
98            // Add the integer component
99            result += &(v.0).1;
100
101            // If the fractional component exists,
102            // append it to the result
103            if let Some(s) = v.1 {
104                result += &(".".to_owned() + &s);
105            }
106
107            result
108        })
109        % "a number"
110}
111
112/// Consumes an array of items
113pub fn array<T: 'static + Clone>(
114    begin: &'static str,
115    item: Parser<T>,
116    end: &'static str,
117) -> Parser<Vec<T>> {
118    seq_no_ws(begin) >> list(item.clone(), seq_no_ws(",")) << seq_no_ws(end)
119        % format!("An array of 0 or more {}(s)", item.expectation)
120}