Sketching out rail and json.
This commit is contained in:
parent
7439ca554c
commit
053d08849e
114
sketching/november_2025/json.dm
Normal file
114
sketching/november_2025/json.dm
Normal 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
|
||||
128
sketching/november_2025/rail.dm
Normal file
128
sketching/november_2025/rail.dm
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user