Sketching out rail and json.

This commit is contained in:
Jesse Brault 2025-11-10 16:32:19 -06:00
parent 7439ca554c
commit 053d08849e
2 changed files with 242 additions and 0 deletions

View File

@ -0,0 +1,114 @@
ns jsonexample
fn main()
let rawJson: String = match io::readFile("myDoc.json")
Success(s) => s,
Failure(err) => exit(err)
end
let o: obj = match parseJsonObj(rawJson)
Success(o) => o,
Failure(err) => exit(err)
end
assertEq!(o.a.b.c[0], 42)
end
fn parseJsonObj(s: String) -> obj
let lexer = JsonLexer(s)
let parser = JsonParser(lexer)
parser.object()
end
enum JsonToken
LBrace,
RBrace,
LSquare,
RSquare,
Comma,
String(String),
Number(Number),
Boolean(Boolean)
end
class String
pub fn <R> matchAt(startIndex: Int, matchers: Map<Regex, R | fn (match: String) -> R), opts: obj {
ignoreWhitespace: boolean,
endIndex: Int
}) -> Option<R>
todo!()
end
end
class JsonLexer(val source: String)
var index: Int
pub fn nextToken() -> Option<JsonToken>
let token: Option<JsonToken> = source.matchAt(index, [
/\{/ => JsonToken::LBrace,
/}/ => JsonToken::RBrace,
/\[/ => JsonToken::LSquare,
/]/ => JsonToken::RSquare,
/[0-9]+/ => { rawNumber: String => JsonToken::Number(number::parse(rawNumber))) }
/true/ => JsonToken::Boolean(true),
/false/ => JsonToken::False(false),
// The following is not a good way for matching strings but whatever
/"%w*"/ => { stringWithQuotes => JsonToken::String(stringWithQuotes[1..-1]) }
], ignoreWhitespace: true)
match token?
JsonToken::LBrace | JsonToken::RBrace | JsonToken::LSquare | JsonToken::RSquare | JsonToken::Comma => do index++ end
JsonToken::String(s) => index += s.len() + 2,
JsonToken::Number(n) => index += n.toString().len(),
JsonToken::Boolean(b) => match b
true => do index += 4 end
false => do index += 5 end
end
end
token
end
end
class JsonParser(val lexer: JsonLexer)
macro expect!($tokenType)
match lexer.nextToken()
Some(JsonToken::$tokenType) => do end,
_ => panic! "Expected ${$tokenType}"
end
end
pub fn object() -> obj
expect!(LBrace)
let result = obj {}
while keyValue() is Some((key, value)) do
result[key] = value
end
expect!(RBrace)
result
end
fn keyValue() -> Option<(String, Any)>
let key = match lexer.nextToken()
Some(JsonToken::String(s)) => s,
_ => return None
end
let value = value()
Some((key, value))
end
fn value() -> Any
match lexer.nextToken()
Some(token) => match token
JsonToken::String(s) => s,
JsonToken::Number(n) => n,
JsonToken::Boolean(b) => b,
JsonToken::LBrace => object(),
JsonToken::LSquare => array()
end,
None => panic! "Expected LBrace, LSquare, String, Number, or Boolean"
end
end
fn array() -> Array<Any>
todo!()
end
end

View File

@ -0,0 +1,128 @@
ns nr::train
use object::{assign, Props, props!}
pub int Train
name: String
primaryColor: Color
fld destination: String
fld volume: Int
fn blowHorn() -> IO
end
pub enum Color
White,
Blue,
Red
end
pub type Printer = fn (message: String) -> IO
pub fn create(props: obj Props<Train> + { printer: Printer }) -> Train =
SimpleTrain(props).with {
volume = 80
}
class SimpleTrain : Train
props!::<Train>()
printer: Printer
ctor(props: obj Props<Train> + { printer: Printer })
assign(self, props)
end
impl fn blowHorn() -> IO
printer("Train ${name} is blowing its horn at volume ${volume}!")
end
end
pub fn createViaObj(props: obj Props<Train>, printer: Printer) -> obj Train
obj {
...props,
printer,
blowHorn: fn () -> IO
printer("Dynamic object Train ${train} is blowing its horn at volume ${self.volume}!")
end
}
end
// props lib file
ns std::core::object
pub fn assign(target: obj, source: obj)
for [key, value] source.props() do
target[key] = value
end
end
pub type Props<T> = obj {
[K in keyof T]: T[K]
}
pub macro fn <T> props!(_: TokenStream) -> TokenStream
let result = TokenStream()
for [key, type] in std::intrinsics::propsAndTypes::<T>() do
result << quote! {
#key: #type
}
end
result
end
// io lib file
ns std::core
pub enum IO<S : Stream = Stream, E = Error> : fn () -> IO<S, E>
Success(stream: S)
impl ()
try
doCall(stream)
catch (e: E)
Failure(e)
end
end
end,
Failure(_)
impl ()
self
end
end;
int
fn <Z : Stream = S> doCall(stream: S) -> IO<Z, E>
end
end
// main example file
ns nr::example
use nr::train::{create, createViaObj, Train, Color}
fn main()
let train = create(obj {
name: 'Test Train',
primaryColor: Color::Blue,
destination: 'Nonavosa Central',
printer: println
})
train.blowHorn()
train.volume = 100
train.blowHorn()
let objTrain: obj Train = createViaObj(obj {
name: 'Obj Train',
primaryColor: Color::Red,
destination: 'Durandana',
printer: println
})
// Since it's an obj, we can dynamically add properties and override methods, similar to JS
objTrain.secondaryColor = Color::White
let originalBlowHorn = objTrain.blowHorn
objTrain.blowHorn = fn ()
originalBlowHorn()
println "Hello again! The train's colors are ${self.primaryColor} and ${self.secondaryColor}!"
end
objTrain.blowHorn()
end