diff --git a/sketching/november_2025/json.dm b/sketching/november_2025/json.dm new file mode 100644 index 0000000..17e4b0c --- /dev/null +++ b/sketching/november_2025/json.dm @@ -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 matchAt(startIndex: Int, matchers: Map R), opts: obj { + ignoreWhitespace: boolean, + endIndex: Int + }) -> Option + todo!() + end +end + +class JsonLexer(val source: String) + var index: Int + + pub fn nextToken() -> Option + let token: Option = 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 + todo!() + end + +end \ No newline at end of file diff --git a/sketching/november_2025/rail.dm b/sketching/november_2025/rail.dm new file mode 100644 index 0000000..11dbdf1 --- /dev/null +++ b/sketching/november_2025/rail.dm @@ -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 + { printer: Printer }) -> Train = + SimpleTrain(props).with { + volume = 80 + } + +class SimpleTrain : Train + props!::() + printer: Printer + + ctor(props: obj Props + { 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, 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 = obj { + [K in keyof T]: T[K] +} + +pub macro fn props!(_: TokenStream) -> TokenStream + let result = TokenStream() + for [key, type] in std::intrinsics::propsAndTypes::() do + result << quote! { + #key: #type + } + end + result +end + +// io lib file +ns std::core + +pub enum IO : fn () -> IO + Success(stream: S) + impl () + try + doCall(stream) + catch (e: E) + Failure(e) + end + end + end, + Failure(_) + impl () + self + end + end; + + int + fn doCall(stream: S) -> IO + 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 \ No newline at end of file