My attempt so far (trying to keep things minimal until I get something working) is the pasted snippet at the bottom.
When running it, it seems to get into some infinite loop but I'm not really sure how to address it? I'm assuming I'm infinitely recursing down the lhs but I'm also not sure how to express it otherwise?
I've tried looking at the documentation and the examples but I can't really figure out how to map them to my use case. I'd appreciate any kind of help and I'll gladly contribute such a lexer/parser as example/testing back upstream if I get something representable working!
import gleam/io
import gleam/list
import gleam/option.{None, Some}
import gleam/set
import nibble.{type Parser}
import nibble/lexer
type TokenT {
// Arithmetic
AddT
SubT
MulT
DivT
ModT
// Relation
LessThanT
LessThanEqT
GreaterThanT
GreaterThanEqT
EqualsT
NotEqualsT
InT
// Atom
IntT(Int)
// AUInt(Int)
FloatT(Float)
StringT(String)
BoolT(Bool)
NullT
// Ident
IdentT(String)
LParen
RParen
}
pub type ArithmeticOp {
Add
Sub
Mul
Div
Mod
}
pub type RelationOp {
LessThan
LessThanEq
GreaterThan
GreaterThanEq
Equals
NotEquals
In
}
pub type Atom {
Int(Int)
Float(Float)
// String(String)
Bool(Bool)
Null
}
pub type Expression {
Arithmetic(Expression, ArithmeticOp, Expression)
Relation(Expression, RelationOp, Expression)
Atom(Atom)
Ident(String)
}
type Context {
// InList
// InMap
// InTernary
InSubExpr
}
fn lexer() {
let reserved_literals = set.from_list(["false", "in", "null", "true"])
let reserved_host =
set.from_list([
"as", "break", "const", "continue", "else", "for", "function", "if",
"import", "let", "loop", "package", "namespace", "return", "var", "void",
"while",
])
let reserved = set.intersection(reserved_literals, reserved_host)
let grouping = [lexer.token("(", LParen), lexer.token(")", RParen)]
let arithmetic_op = [
lexer.token("+", AddT),
lexer.token("-", SubT),
lexer.token("*", MulT),
lexer.token("/", DivT),
lexer.token("%", ModT),
]
let relation_op = [
// Relation
lexer.token("<", LessThanT),
lexer.token(">", GreaterThanT),
lexer.token("<=", LessThanEqT),
lexer.token(">=", GreaterThanEqT),
lexer.token("==", EqualsT),
lexer.token("!=", NotEqualsT),
lexer.token("in", InT),
]
let value = [
// Value
lexer.token("true", BoolT(True)),
lexer.token("false", BoolT(False)),
lexer.number(IntT, FloatT),
// lexer.then -> UInt
lexer.string("\"", StringT),
lexer.string("'", StringT),
lexer.token("null", NullT),
]
lexer.simple(
list.flatten([
grouping,
arithmetic_op,
relation_op,
value,
[
// Identifier
lexer.identifier("[_a-zA-Z][_a-zA-Z0-9]*", "[^\\s=]", reserved, IdentT),
// Comments
lexer.comment("//", fn(_) { Nil }) |> lexer.ignore(),
// Whitespace
lexer.whitespace(Nil)
|> lexer.ignore(),
],
]),
)
}
fn parser() -> Parser(Expression, TokenT, Context) {
use expr <- nibble.do(arith_add_sub_full_parser())
nibble.succeed(expr)
}
fn arith_add_sub_full_parser() -> Parser(Expression, TokenT, Context) {
nibble.one_of([arith_add_sub_inner_parser(), arith_mul_div_mod_full_parser()])
}
fn arith_add_sub_inner_parser() -> Parser(Expression, TokenT, Context) {
use lhs <- nibble.do(nibble.lazy(arith_add_sub_full_parser))
use op <- nibble.do(op_add_sub_parser())
use rhs <- nibble.do(arith_mul_div_mod_full_parser())
nibble.return(Arithmetic(lhs, op, rhs))
}
fn arith_mul_div_mod_full_parser() -> Parser(Expression, TokenT, Context) {
nibble.one_of([arith_mul_div_mod_inner_parser(), atom_parser()])
}
fn arith_mul_div_mod_inner_parser() -> Parser(Expression, TokenT, Context) {
use lhs <- nibble.do(nibble.lazy(arith_mul_div_mod_full_parser))
use op <- nibble.do(op_mul_div_mod_parser())
use rhs <- nibble.do(atom_parser())
nibble.return(Arithmetic(lhs, op, rhs))
}
fn op_mul_div_mod_parser() -> Parser(ArithmeticOp, TokenT, Context) {
use tok <- nibble.take_map("*,/,%")
case tok {
MulT -> Some(Mul)
DivT -> Some(Div)
ModT -> Some(Mod)
_ -> None
}
}
fn op_add_sub_parser() -> Parser(ArithmeticOp, TokenT, Context) {
use tok <- nibble.take_map("+,-")
case tok {
AddT -> Some(Add)
SubT -> Some(Sub)
_ -> None
}
}
fn atom_parser() -> Parser(Expression, TokenT, Context) {
nibble.backtrackable({
use t <- nibble.do(nibble.any())
case t {
IntT(n) -> nibble.return(Atom(Int(n)))
FloatT(n) -> nibble.return(Atom(Float(n)))
BoolT(n) -> nibble.return(Atom(Bool(n)))
NullT -> nibble.return(Atom(Null))
_ -> nibble.fail("Expected a literal value")
}
})
}
pub fn main() {
let assert Ok(lexed) = lexer.run("5 + a", lexer())
let _ =
nibble.run(lexed, parser())
|> io.debug
"done"
}
Hi!
This seems like a really nice lib and I've tried to use it for a quite simple expression parser, inspired by clarkmcc/cel-rust.
My attempt so far (trying to keep things minimal until I get something working) is the pasted snippet at the bottom.
When running it, it seems to get into some infinite loop but I'm not really sure how to address it? I'm assuming I'm infinitely recursing down the
lhsbut I'm also not sure how to express it otherwise?I've tried looking at the documentation and the examples but I can't really figure out how to map them to my use case. I'd appreciate any kind of help and I'll gladly contribute such a lexer/parser as example/testing back upstream if I get something representable working!