basic_dsl/
lib.rs

1// SPDX-License-Identifier: EUPL-1.2
2// Copyright (c) 2025 The BASIC DSL Contributors
3
4/*!
5# BASIC DSL
6
7A procedural macro crate that provides a BASIC interpreter DSL embedded in Rust.
8
9## Usage
10
11```rust
12use basic_dsl::basic;
13
14basic!(r#"
15    10 LET X = 1
16    20 PRINT X
17    30 IF X < 5 THEN GOTO 50
18    40 END
19    50 LET X = X + 1
20    60 GOTO 20
21"#);
22```
23
24The macro supports:
25- Variable assignment with `LET`
26- Printing with `PRINT` (numbers and string literals)
27- Conditional jumps with `IF...THEN GOTO`
28- Unconditional jumps with `GOTO`
29- Line labels and program termination with `END`
30*/
31
32mod ast;
33mod codegen;
34mod parser;
35
36use proc_macro::TokenStream;
37use syn::{Result, spanned::Spanned};
38
39use crate::codegen::generate_runtime_code;
40use crate::parser::parse_basic_program;
41
42/// Main entry point for the BASIC DSL macro.
43///
44/// Accepts only string literals containing BASIC program code.
45#[proc_macro]
46pub fn basic(input: TokenStream) -> TokenStream {
47    match expand_from_lit(input) {
48        Ok(ts) => ts.into(),
49        Err(e) => e.to_compile_error().into(),
50    }
51}
52
53fn expand_from_lit(input: TokenStream) -> Result<proc_macro2::TokenStream> {
54    let expr: syn::Expr = syn::parse(input)?;
55
56    let lit = match expr {
57        syn::Expr::Lit(syn::ExprLit {
58            lit: syn::Lit::Str(lit),
59            ..
60        }) => lit,
61        other => return Err(syn::Error::new(other.span(), "expected a string literal")),
62    };
63
64    expand_basic(&lit)
65}
66
67fn expand_basic(src: &syn::LitStr) -> Result<proc_macro2::TokenStream> {
68    let code = src.value();
69    let stmts = parse_basic_program(&code, src)?;
70    generate_runtime_code(stmts, src)
71}