or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

ast-models.mdcode-generation.mdconfiguration.mdcore-parsing.mdexceptions.mdindex.mdsemantic-actions.mdtree-walking.md

semantic-actions.mddocs/

0

# Semantic Actions

1

2

Build custom semantic actions to transform parse results, construct object models, and implement domain-specific processing during parsing. Semantic actions enable converting raw parse trees into meaningful data structures and executing computations during the parsing process.

3

4

## Capabilities

5

6

### Basic Semantic Actions

7

8

Transform parse results by defining methods that correspond to grammar rule names.

9

10

```python { .api }

11

class ASTSemantics:

12

"""

13

Basic AST building semantics for parse tree construction.

14

15

Provides default implementations for common AST operations:

16

- group(): Handle grouped expressions

17

- element(): Process individual elements

18

- sequence(): Build sequences of elements

19

- choice(): Select from choice alternatives

20

"""

21

22

def group(self, ast):

23

"""Handle grouped expressions, typically removing grouping parentheses."""

24

25

def element(self, ast):

26

"""Process individual grammar elements."""

27

28

def sequence(self, ast):

29

"""Build sequences of parsed elements."""

30

31

def choice(self, ast):

32

"""Select result from choice alternatives."""

33

```

34

35

Usage example:

36

37

```python

38

import tatsu

39

40

grammar = '''

41

expr = term ("+" term)*;

42

term = factor ("*" factor)*;

43

factor = "(" expr ")" | number;

44

number = /\d+/;

45

'''

46

47

class BasicSemantics:

48

def number(self, ast):

49

return int(ast)

50

51

def factor(self, ast):

52

# Handle parenthesized expressions

53

if len(ast) == 3: # "(" expr ")"

54

return ast[1] # Return the inner expression

55

return ast # Return the number directly

56

57

def term(self, ast):

58

result = ast[0]

59

for op, operand in ast[1]:

60

result *= operand

61

return result

62

63

def expr(self, ast):

64

result = ast[0]

65

for op, operand in ast[1]:

66

result += operand

67

return result

68

69

model = tatsu.compile(grammar)

70

result = model.parse("2 + 3 * 4", semantics=BasicSemantics())

71

print(result) # 14

72

```

73

74

### Model Builder Semantics

75

76

Automatically construct object model instances from parse results with type registration and inheritance support.

77

78

```python { .api }

79

class ModelBuilderSemantics:

80

"""

81

Object model building semantics with type registration.

82

83

Features:

84

- Automatic object creation from grammar rules

85

- Type registration for custom classes

86

- Base type inheritance for all model objects

87

- Constructor parameter mapping

88

"""

89

90

def __init__(self, context=None, base_type=None, types=None):

91

"""

92

Initialize model builder semantics.

93

94

Parameters:

95

- context: parsing context object

96

- base_type (type, optional): Base class for generated nodes (default: Node)

97

- types (dict, optional): Rule name to type mappings

98

"""

99

100

def _register_constructor(self, rule_name, constructor):

101

"""Register a constructor function for a specific rule."""

102

103

def _find_existing_constructor(self, rule_name):

104

"""Find existing constructor for rule name."""

105

106

def _get_constructor(self, rule_name):

107

"""Get or create constructor for rule name."""

108

```

109

110

Usage example:

111

112

```python

113

import tatsu

114

from tatsu.semantics import ModelBuilderSemantics

115

from tatsu.objectmodel import Node

116

117

grammar = '''

118

program::Program = statement*;

119

statement::Statement = assignment | expression;

120

assignment::Assignment = identifier "=" expression;

121

expression::Expression = identifier | number;

122

identifier::Identifier = /[a-zA-Z][a-zA-Z0-9]*/;

123

number::Number = /\d+/;

124

'''

125

126

class MyNode(Node):

127

def __repr__(self):

128

return f"{self.__class__.__name__}({dict(self)})"

129

130

# Custom type mappings

131

class Program(MyNode): pass

132

class Statement(MyNode): pass

133

class Assignment(MyNode): pass

134

class Expression(MyNode): pass

135

class Identifier(MyNode): pass

136

class Number(MyNode): pass

137

138

type_map = {

139

'Program': Program,

140

'Statement': Statement,

141

'Assignment': Assignment,

142

'Expression': Expression,

143

'Identifier': Identifier,

144

'Number': Number

145

}

146

147

semantics = ModelBuilderSemantics(base_type=MyNode, types=type_map)

148

model = tatsu.compile(grammar)

149

result = model.parse("x = 42", semantics=semantics)

150

print(result) # Program containing Assignment with Identifier and Number

151

```

152

153

### Custom Semantic Actions

154

155

Implement domain-specific logic and transformations during parsing.

156

157

```python { .api }

158

# Custom semantic action patterns

159

160

class CalculatorSemantics:

161

"""Example: Calculator with immediate evaluation."""

162

163

def number(self, ast):

164

"""Convert number tokens to integers."""

165

return int(ast)

166

167

def factor(self, ast):

168

"""Handle factors: numbers or parenthesized expressions."""

169

if isinstance(ast, list) and len(ast) == 3:

170

return ast[1] # Return content of parentheses

171

return ast

172

173

def term(self, ast):

174

"""Handle multiplication and division."""

175

result = ast[0]

176

for operator, operand in ast[1]:

177

if operator == '*':

178

result *= operand

179

elif operator == '/':

180

result /= operand

181

return result

182

183

def expr(self, ast):

184

"""Handle addition and subtraction."""

185

result = ast[0]

186

for operator, operand in ast[1]:

187

if operator == '+':

188

result += operand

189

elif operator == '-':

190

result -= operand

191

return result

192

```

193

194

### Semantic Action Error Handling

195

196

Handle errors and validation within semantic actions.

197

198

```python { .api }

199

from tatsu.exceptions import FailedSemantics

200

201

class ValidatingSemantics:

202

"""Semantic actions with validation and error handling."""

203

204

def number(self, ast):

205

try:

206

value = int(ast)

207

if value < 0:

208

raise FailedSemantics(f"Negative numbers not allowed: {value}")

209

return value

210

except ValueError as e:

211

raise FailedSemantics(f"Invalid number format: {ast}") from e

212

213

def division(self, ast):

214

left, operator, right = ast

215

if operator == '/' and right == 0:

216

raise FailedSemantics("Division by zero is not allowed")

217

return left / right

218

219

def variable_ref(self, ast):

220

var_name = str(ast)

221

if var_name not in self.variables:

222

raise FailedSemantics(f"Undefined variable: {var_name}")

223

return self.variables[var_name]

224

225

def __init__(self):

226

self.variables = {}

227

```

228

229

### Context-Aware Semantic Actions

230

231

Access parsing context and maintain state across semantic actions.

232

233

```python { .api }

234

class ContextAwareSemantics:

235

"""Semantic actions with context and state management."""

236

237

def __init__(self):

238

self.symbol_table = {}

239

self.scope_stack = [{}]

240

self.current_scope = self.scope_stack[-1]

241

242

def enter_scope(self, ast):

243

"""Enter a new lexical scope."""

244

new_scope = {}

245

self.scope_stack.append(new_scope)

246

self.current_scope = new_scope

247

return ast

248

249

def exit_scope(self, ast):

250

"""Exit current lexical scope."""

251

if len(self.scope_stack) > 1:

252

self.scope_stack.pop()

253

self.current_scope = self.scope_stack[-1]

254

return ast

255

256

def variable_declaration(self, ast):

257

"""Handle variable declarations."""

258

var_name, _, value = ast

259

if var_name in self.current_scope:

260

raise FailedSemantics(f"Variable '{var_name}' already declared in current scope")

261

self.current_scope[var_name] = value

262

return ast

263

264

def variable_reference(self, ast):

265

"""Handle variable references with scope resolution."""

266

var_name = str(ast)

267

268

# Search scope stack from innermost to outermost

269

for scope in reversed(self.scope_stack):

270

if var_name in scope:

271

return scope[var_name]

272

273

raise FailedSemantics(f"Undefined variable: {var_name}")

274

```

275

276

### Advanced Semantic Patterns

277

278

```python { .api }

279

class AdvancedSemantics:

280

"""Advanced semantic action patterns."""

281

282

def __init__(self):

283

self.type_checker = TypeChecker()

284

self.optimizer = ASTOptimizer()

285

286

def typed_expression(self, ast):

287

"""Type checking during parsing."""

288

expr_type = self.type_checker.infer_type(ast)

289

return TypedExpression(ast, expr_type)

290

291

def optimized_expression(self, ast):

292

"""AST optimization during parsing."""

293

return self.optimizer.optimize(ast)

294

295

def macro_expansion(self, ast):

296

"""Macro expansion during parsing."""

297

if self.is_macro_call(ast):

298

return self.expand_macro(ast)

299

return ast

300

301

def code_generation(self, ast):

302

"""Direct code generation from AST."""

303

return self.code_generator.generate(ast)

304

```

305

306

## Integration Patterns

307

308

### Using Multiple Semantic Approaches

309

310

```python

311

# Combine different semantic approaches

312

class HybridSemantics(ModelBuilderSemantics):

313

"""Combine model building with custom transformations."""

314

315

def __init__(self):

316

super().__init__(base_type=MyNode)

317

self.evaluator = ExpressionEvaluator()

318

319

def constant_expression(self, ast):

320

"""Evaluate constants at parse time."""

321

if self.is_constant(ast):

322

return ConstantNode(value=self.evaluator.evaluate(ast))

323

return super().constant_expression(ast)

324

```

325

326

### Semantic Action Composition

327

328

```python

329

class CompositeSemantics:

330

"""Compose multiple semantic action objects."""

331

332

def __init__(self, *semantics_objects):

333

self.semantics = semantics_objects

334

335

def __getattr__(self, name):

336

"""Delegate to first semantics object that has the method."""

337

for sem in self.semantics:

338

if hasattr(sem, name):

339

return getattr(sem, name)

340

raise AttributeError(f"No semantic action found for: {name}")

341

342

# Usage

343

combined = CompositeSemantics(

344

CalculatorSemantics(),

345

ValidatingSemantics(),

346

ModelBuilderSemantics()

347

)

348

```

349

350

### Performance Considerations

351

352

```python

353

class OptimizedSemantics:

354

"""Performance-optimized semantic actions."""

355

356

def __init__(self):

357

# Pre-compile regex patterns

358

self.number_pattern = re.compile(r'\d+')

359

# Cache frequently used objects

360

self.node_cache = {}

361

362

def number(self, ast):

363

"""Optimized number parsing with caching."""

364

if ast in self.node_cache:

365

return self.node_cache[ast]

366

367

result = int(ast)

368

if len(self.node_cache) < 1000: # Limit cache size

369

self.node_cache[ast] = result

370

return result

371

372

def list_expression(self, ast):

373

"""Efficient list building."""

374

if not ast[1]: # Empty list case

375

return []

376

377

# Use list comprehension for better performance

378

return [ast[0]] + [item for _, item in ast[1]]

379

```