Skip to content

Commit fd9e01b

Browse files
authored
Merge branch 'main' into bolt-builder-pred-opt-6542923888462175577
2 parents df19764 + 1e5e176 commit fd9e01b

23 files changed

Lines changed: 521 additions & 369 deletions

File tree

‎.jules/bolt.md‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,4 @@
2020
## 2024-08-01 - Bytecode Disassembler String Allocation Optimization
2121
**Learning:** Formatting directly into a string buffer inside a tight loop with `write!(buffer, ...)` avoids unnecessary string heap allocations compared to `buffer.push_str(&format!(...))`.
2222
**Action:** Always prefer formatting directly into the target String buffer when concatenating strings in loops in performance-sensitive paths like debuggers or disassemblers.
23-
## 2024-03-24 - Bolt: O(N) block predecessor mapping optimization
24-
**Learning:** O(N^2) behavior in compiler IR graph traversals (`contains` calls inside loops) can severely degrade performance on large inputs. Grouping by target first into a `HashMap` reduces it to O(N).
25-
**Action:** When reconstructing graph edges (predecessors/successors) or processing lists against each other, group items with a HashMap or IndexMap instead of using nested loops.
23+

‎Cargo.lock‎

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎compiler/ast/tests/ast_tests.rs‎

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use techscript_ast::{
2-
AssignmentExpr, EnumDecl, EnumVariant, Expression, FieldSpec, Ident, LiteralExpr, LiteralVal,
3-
Pattern, StructDecl, VarDecl,
2+
AssignmentExpr, Block, BreakStmt, EnumDecl, EnumVariant, Expression, FieldSpec, Ident,
3+
LiteralExpr, LiteralVal, Pattern, Program, Statement, StructDecl, VarDecl,
44
};
55
use techscript_common::{NodeId, Span};
66

@@ -101,5 +101,47 @@ fn test_ast_serialization() {
101101
assert_eq!(lit, deserialized);
102102
}
103103

104+
#[test]
105+
fn test_ast_program_construction_and_serialization() {
106+
let span = Span::new(0, 50);
107+
let id = NodeId(1);
108+
109+
// Create a dummy statement (Break)
110+
let break_stmt = Statement::Break(BreakStmt::new(NodeId(2), span));
111+
112+
let program = Program::new(id, vec![break_stmt.clone()], span);
113+
114+
assert_eq!(program.id, id);
115+
assert_eq!(program.statements.len(), 1);
116+
assert_eq!(program.span, span);
117+
118+
let serialized = serde_json::to_string(&program).expect("serialize should succeed");
119+
let deserialized: Program =
120+
serde_json::from_str(&serialized).expect("deserialize should succeed");
121+
122+
assert_eq!(program, deserialized);
123+
}
124+
125+
#[test]
126+
fn test_ast_block_construction_and_serialization() {
127+
let span = Span::new(10, 20);
128+
let id = NodeId(3);
129+
130+
// Create a dummy statement (Break)
131+
let break_stmt = Statement::Break(BreakStmt::new(NodeId(4), span));
132+
133+
let block = Block::new(id, vec![break_stmt.clone()], span);
134+
135+
assert_eq!(block.id, id);
136+
assert_eq!(block.statements.len(), 1);
137+
assert_eq!(block.span, span);
138+
139+
let serialized = serde_json::to_string(&block).expect("serialize should succeed");
140+
let deserialized: Block =
141+
serde_json::from_str(&serialized).expect("deserialize should succeed");
142+
143+
assert_eq!(block, deserialized);
144+
}
145+
104146
// Internal helper just to satisfy TypeSpec compilation in test_ast_struct_decl
105147
use techscript_ast::TypeSpec;

‎compiler/lexer/src/lib.rs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use techscript_syntax::{lookup_keyword, Token, TokenKind};
1212
/// Private token enumeration used internally by Logos for scanning.
1313
#[derive(Logos, Debug, Clone, Copy, PartialEq, Eq)]
1414
#[logos(skip r"[ \t\r]+")] // Skip spaces, tabs, and carriage returns
15-
#[allow(dead_code)]
1615
enum LogosToken {
1716
#[token("\n")]
1817
#[token("\r\n")]

‎compiler/semantic/src/dsl_schema.rs‎

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,7 @@ fn register_web_schemas(reg: &mut HashMap<String, DSLSchema>) {
298298
);
299299
}
300300

301-
fn register_canvas_schemas(reg: &mut HashMap<String, DSLSchema>) {
302-
// Canvas module schemas ──────────────────────────────────────────
301+
fn register_canvas_shape_schemas(reg: &mut HashMap<String, DSLSchema>) {
303302
reg.insert(
304303
"logo".to_string(),
305304
DSLSchema::new(
@@ -318,22 +317,6 @@ fn register_canvas_schemas(reg: &mut HashMap<String, DSLSchema>) {
318317
),
319318
);
320319

321-
reg.insert(
322-
"rings".to_string(),
323-
DSLSchema::new(
324-
vec![
325-
"count".into(),
326-
"color".into(),
327-
"size".into(),
328-
"thickness".into(),
329-
"spacing".into(),
330-
"rotation".into(),
331-
],
332-
vec![],
333-
vec![],
334-
),
335-
);
336-
337320
reg.insert(
338321
"emblem".to_string(),
339322
DSLSchema::new(
@@ -365,7 +348,9 @@ fn register_canvas_schemas(reg: &mut HashMap<String, DSLSchema>) {
365348
vec![],
366349
),
367350
);
351+
}
368352

353+
fn register_canvas_text_schemas(reg: &mut HashMap<String, DSLSchema>) {
369354
reg.insert(
370355
"letter".to_string(),
371356
DSLSchema::new(
@@ -383,21 +368,6 @@ fn register_canvas_schemas(reg: &mut HashMap<String, DSLSchema>) {
383368
),
384369
);
385370

386-
reg.insert(
387-
"circuits".to_string(),
388-
DSLSchema::new(
389-
vec![
390-
"color".into(),
391-
"density".into(),
392-
"width".into(),
393-
"animated".into(),
394-
"complexity".into(),
395-
],
396-
vec![],
397-
vec![],
398-
),
399-
);
400-
401371
reg.insert(
402372
"title".to_string(),
403373
DSLSchema::new(
@@ -437,6 +407,39 @@ fn register_canvas_schemas(reg: &mut HashMap<String, DSLSchema>) {
437407
vec![],
438408
),
439409
);
410+
}
411+
412+
fn register_canvas_misc_schemas(reg: &mut HashMap<String, DSLSchema>) {
413+
reg.insert(
414+
"rings".to_string(),
415+
DSLSchema::new(
416+
vec![
417+
"count".into(),
418+
"color".into(),
419+
"size".into(),
420+
"thickness".into(),
421+
"spacing".into(),
422+
"rotation".into(),
423+
],
424+
vec![],
425+
vec![],
426+
),
427+
);
428+
429+
reg.insert(
430+
"circuits".to_string(),
431+
DSLSchema::new(
432+
vec![
433+
"color".into(),
434+
"density".into(),
435+
"width".into(),
436+
"animated".into(),
437+
"complexity".into(),
438+
],
439+
vec![],
440+
vec![],
441+
),
442+
);
440443

441444
reg.insert(
442445
"theme".to_string(),
@@ -487,6 +490,13 @@ fn register_canvas_schemas(reg: &mut HashMap<String, DSLSchema>) {
487490
);
488491
}
489492

493+
fn register_canvas_schemas(reg: &mut HashMap<String, DSLSchema>) {
494+
// Canvas module schemas ──────────────────────────────────────────
495+
register_canvas_shape_schemas(reg);
496+
register_canvas_text_schemas(reg);
497+
register_canvas_misc_schemas(reg);
498+
}
499+
490500
fn register_generic_schemas(reg: &mut HashMap<String, DSLSchema>) {
491501
// Generic DSL blocks ────────────────────────────────────────────
492502
reg.insert(

‎compiler/syntax/Cargo.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ description = "Token definitions, keywords, and operator precedence for TechScri
77
[dependencies]
88
techscript_common = { path = "../common" }
99
serde = { workspace = true }
10+
strum = "0.28.0"
11+
strum_macros = "0.28.0"
1012

1113
[dev-dependencies]
1214
serde_json = { workspace = true }

‎compiler/syntax/src/token_kind.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ use serde::{Deserialize, Serialize};
55
use std::fmt;
66

77
/// Classification of literal values in TechScript 2.0.
8-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8+
#[derive(
9+
Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, strum_macros::EnumIter,
10+
)]
911
pub enum LiteralKind {
1012
/// Integer literal (e.g., `42`).
1113
Int,
@@ -50,7 +52,9 @@ impl fmt::Display for NumericLiteralKind {
5052
/// - **Canonical 2.0**: The one true spelling — no warnings emitted.
5153
/// - **Deprecated Alias**: Old spelling — still parsed, but emits a `TSW1xxx` warning.
5254
/// - **Reserved**: Recognised by the lexer but not yet active.
53-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
55+
#[derive(
56+
Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, strum_macros::EnumIter,
57+
)]
5458
pub enum TokenKind {
5559
// ── Canonical 2.0 Keywords ──────────────────────────────────────────────────────
5660
/// `do` — function/method declaration (replaces `build`, `fun`, `function`)

‎compiler/syntax/tests/syntax_tests.rs‎

Lines changed: 12 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -378,114 +378,20 @@ fn test_serde_roundtrips() {
378378

379379
#[test]
380380
fn test_token_uniqueness() {
381+
use strum::IntoEnumIterator;
381382
// Ensure all TokenKind static_lexemes are unique
382383
let mut lexemes = HashSet::new();
383-
let kinds = [
384-
TokenKind::Do,
385-
TokenKind::Send,
386-
TokenKind::When,
387-
TokenKind::Loop,
388-
TokenKind::Repeat,
389-
TokenKind::For,
390-
TokenKind::In,
391-
TokenKind::Match,
392-
TokenKind::Case,
393-
TokenKind::Default,
394-
TokenKind::Try,
395-
TokenKind::Catch,
396-
TokenKind::Throw,
397-
TokenKind::Use,
398-
TokenKind::Class,
399-
TokenKind::Struct,
400-
TokenKind::Enum,
401-
TokenKind::Trait,
402-
TokenKind::Interface,
403-
TokenKind::Const,
404-
TokenKind::Null,
405-
TokenKind::Say,
406-
TokenKind::Ask,
407-
TokenKind::Break,
408-
TokenKind::Continue,
409-
TokenKind::Else,
410-
TokenKind::Async,
411-
TokenKind::Await,
412-
TokenKind::Parallel,
413-
TokenKind::End,
414-
TokenKind::Export,
415-
TokenKind::New,
416-
TokenKind::SelfKw,
417-
TokenKind::True,
418-
TokenKind::False,
419-
TokenKind::Typeof,
420-
TokenKind::With,
421-
TokenKind::Build,
422-
TokenKind::Make,
423-
TokenKind::Return,
424-
TokenKind::Model,
425-
TokenKind::If,
426-
TokenKind::Elif,
427-
TokenKind::While,
428-
TokenKind::Import,
429-
TokenKind::From,
430-
TokenKind::Let,
431-
TokenKind::Var,
432-
TokenKind::Fun,
433-
TokenKind::Function,
434-
TokenKind::Attempt,
435-
TokenKind::None,
436-
TokenKind::Keep,
437-
TokenKind::Give,
438-
TokenKind::Stop,
439-
TokenKind::Skip,
440-
TokenKind::Each,
441-
TokenKind::Switch,
442-
TokenKind::Be,
443-
TokenKind::Equals,
444-
TokenKind::Then,
445-
TokenKind::Plus,
446-
TokenKind::Minus,
447-
TokenKind::Star,
448-
TokenKind::Slash,
449-
TokenKind::DoubleSlash,
450-
TokenKind::Percent,
451-
TokenKind::DoubleStar,
452-
TokenKind::EqualEqual,
453-
TokenKind::BangEqual,
454-
TokenKind::TripleEqual,
455-
TokenKind::BangEqualEqual,
456-
TokenKind::Less,
457-
TokenKind::Greater,
458-
TokenKind::LessEqual,
459-
TokenKind::GreaterEqual,
460-
TokenKind::Equal,
461-
TokenKind::PlusEqual,
462-
TokenKind::MinusEqual,
463-
TokenKind::StarEqual,
464-
TokenKind::SlashEqual,
465-
TokenKind::PercentEqual,
466-
TokenKind::DotDot,
467-
TokenKind::DotDotEqual,
468-
TokenKind::QuestionDot,
469-
TokenKind::QuestionQuestion,
470-
TokenKind::Arrow,
471-
TokenKind::LeftParen,
472-
TokenKind::RightParen,
473-
TokenKind::LeftBrace,
474-
TokenKind::RightBrace,
475-
TokenKind::LeftBracket,
476-
TokenKind::RightBracket,
477-
TokenKind::Comma,
478-
TokenKind::Dot,
479-
TokenKind::Colon,
480-
TokenKind::Semicolon,
481-
];
482384

483-
for kind in kinds {
484-
let lexeme = kind.static_lexeme().unwrap();
485-
assert!(
486-
lexemes.insert(lexeme),
487-
"Duplicate static lexeme detected for: {:?}",
488-
kind
489-
);
385+
for kind in TokenKind::iter() {
386+
if kind == TokenKind::FStringExprStart || kind == TokenKind::FStringExprEnd {
387+
continue; // Overlaps with LeftBrace/RightBrace logically, but handled contextually
388+
}
389+
if let Some(lexeme) = kind.static_lexeme() {
390+
assert!(
391+
lexemes.insert(lexeme),
392+
"Duplicate static lexeme detected for: {:?}",
393+
kind
394+
);
395+
}
490396
}
491397
}

0 commit comments

Comments
 (0)