Refactor and error handling for compiler

pull/3/head
Avery 3 weeks ago committed by itycodes
parent 1e1d0911e4
commit 9aafb9758f

@ -19,7 +19,7 @@ fn test_emit() {
1.0)))
";
let ast = parse(tokenize(src));
let module = meta_compile(&mut ast.unwrap());
let module = meta_compile(ast.unwrap()).unwrap();
let res = spirv_meta(module);
println!("{}", res);
}

@ -0,0 +1,206 @@
use crate::{
compiler::{
AddressingModel, Capability, EntryPoint, ExecutionMode, ExecutionModel, Memory,
MemoryModel, expect_list, expect_one_of, expect_symbol,
},
parser::{Ast, Localised, Location},
};
use super::{
BuiltinDecoration, CompilerError, Decoration, Function, GlobalVariable, Import, Module,
StorageClass, expect_empty, expect_empty_ast,
};
pub fn compile_module<I: Iterator<Item = Ast>>(
module: &mut Module,
mut list: I,
loc: Location,
) -> Result<(), CompilerError> {
let _cap = expect_one_of(&["Shader"], expect_symbol(list.next(), &loc)?)?;
let _exec = expect_one_of(&["Logical"], expect_symbol(list.next(), &loc)?)?;
let _memory = expect_one_of(&["GLSL450"], expect_symbol(list.next(), &loc)?)?;
module.memory_model = Memory {
addressing_model: AddressingModel::Logical,
memory_model: MemoryModel::GLSL450,
};
module.capabilities.push(Capability::Shader);
expect_empty_ast(list)
}
pub fn compile_import<I: Iterator<Item = Ast>>(
module: &mut Module,
mut list: I,
loc: Location,
) -> Result<(), CompilerError> {
let name = expect_symbol(list.next(), &loc)?.into_inner();
let value = expect_symbol(list.next(), &loc)?.into_inner();
module.imports.push(Import { name, value });
expect_empty_ast(list)
}
pub fn compile_bind<I: Iterator<Item = Ast>>(
module: &mut Module,
mut list: I,
loc: Location,
) -> Result<(), CompilerError> {
let Localised {
location: name_and_type_loc,
item: name_and_type,
} = expect_list(list.next(), &loc)?;
let name_and_type = name_and_type
.into_iter()
.map(|x| expect_symbol(Some(x), &name_and_type_loc).map(Localised::into_inner))
.collect::<Result<String, CompilerError>>()?;
let mut name_and_type = name_and_type.split(':').map(|s| s.to_string());
// name_and_type is of the name:type format, like foo:f32
let name: String = name_and_type
.next()
.ok_or(CompilerError::ExpectedSymbol(name_and_type_loc.clone()))?;
let typ: String = name_and_type
.next()
.ok_or(CompilerError::ExpectedType(name_and_type_loc.tail()))?;
expect_empty(name_and_type, name_and_type_loc)?;
let Localised {
location: bind_loc,
item: bind,
} = expect_list(list.next(), &loc)?;
let bind: Vec<String> = bind
.into_iter()
.map(|x| expect_symbol(Some(x), &bind_loc).map(Localised::into_inner))
.collect::<Result<_, _>>()?;
let bind_name = bind
.get(0)
.ok_or(CompilerError::ExpectedSymbol(bind_loc.clone()))?;
let bind_name = match bind_name.as_str() {
"BuiltIn" => Decoration::BuiltIn(BuiltinDecoration::FragCoord),
"Location" => Decoration::Location(bind.get(1).unwrap().parse::<u32>().unwrap()),
b => return Err(CompilerError::UnknownBind(b.to_string(), bind_loc)),
};
let mut exists = false;
for var in &module.globals {
if var.name == name {
exists = true;
}
}
if !exists {
module.globals.push(GlobalVariable {
name,
typ,
storage_class: StorageClass::Undefined,
decorations: vec![bind_name],
});
}
expect_empty_ast(list)
}
pub fn compile_dec<I: Iterator<Item = Ast>>(
module: &mut Module,
mut list: I,
loc: Location,
) -> Result<(), CompilerError> {
let Localised {
location: name_and_type_loc,
item: name_and_type,
} = expect_symbol(list.next(), &loc)?;
let mut name_and_type = name_and_type.split(':').map(|s| s.to_string());
let name: String = name_and_type
.next()
.ok_or(CompilerError::ExpectedSymbol(name_and_type_loc.clone()))?;
let typ: String = name_and_type
.next()
.ok_or(CompilerError::ExpectedType(name_and_type_loc.tail()))?;
expect_empty(name_and_type, name_and_type_loc)?;
let storage_class = expect_symbol(list.next(), &loc)?;
let mut exists = false;
for var in &module.globals {
if var.name.as_str() == name.as_str() {
exists = true;
}
}
let storage_class = match storage_class.as_str() {
"Input" => StorageClass::Input,
"Output" => StorageClass::Output,
_ => StorageClass::Undefined,
};
if !exists {
module.globals.push(GlobalVariable {
name: name.to_string(),
typ: typ.to_string(),
storage_class: storage_class.clone(),
decorations: vec![],
});
}
if exists {
module
.globals
.iter_mut()
.find(|x| x.name.as_str() == name.as_str())
.unwrap()
.storage_class = storage_class.clone();
}
expect_empty_ast(list)
}
pub fn compile_entry<I: Iterator<Item = Ast>>(
module: &mut Module,
mut list: I,
loc: Location,
) -> Result<(), CompilerError> {
let name = expect_symbol(list.next(), &loc)?;
let _exec_model = expect_one_of(&["Fragment"], expect_symbol(list.next(), &loc)?)?;
let _exec_mode = expect_one_of(&["OriginUpperLeft"], expect_symbol(list.next(), &loc)?)?;
let Localised {
location: interface_loc,
item: interface,
} = expect_list(list.next(), &loc)?;
let interface: Vec<String> = interface
.into_iter()
.map(|x| expect_symbol(Some(x), &interface_loc).map(|s| s.into_inner().replace(":", "")))
.collect::<Result<_, _>>()?;
module.entry_points.push(EntryPoint {
execution_model: ExecutionModel::Fragment,
execution_mode: ExecutionMode::OriginUpperLeft,
name: name.to_string(),
interface,
});
expect_empty_ast(list)
}
pub fn compile_fun<I: Iterator<Item = Ast>>(
module: &mut Module,
mut list: I,
loc: Location,
) -> Result<(), CompilerError> {
let Localised {
location: name_loc,
item: name,
} = expect_list(list.next(), &loc)?;
let mut name_it = name.into_iter();
let name = expect_symbol(name_it.next(), &name_loc)?;
expect_empty(name_it, name_loc)?;
let body = list.collect::<Vec<_>>();
let location = if let (Some(s), Some(e)) = (
body.first().map(|a| a.location()),
body.last().map(|a| a.location()),
) {
Location::range(s, e)
} else {
Location::Char { line: 0, col: 0 }
};
let fun = Function {
name: name.to_string(),
return_type: "void".to_string(),
arguments: vec![],
body: Some(vec![]),
ast: Some(Ast::List(Localised {
location,
item: body,
})),
};
module.functions.push(fun);
Ok(())
}

@ -1,6 +1,11 @@
use meta_compile::{
compile_bind, compile_dec, compile_entry, compile_fun, compile_import, compile_module,
};
use crate::parser::{Ast, Localised, Location};
pub mod backend;
mod meta_compile;
use std::fmt;
@ -127,215 +132,111 @@ pub struct Module {
pub imports: Vec<Import>,
}
pub fn meta_compile(ast: &mut Ast) -> Module {
let mut module: Module = Module::default();
#[derive(Debug)]
pub enum CompilerError {
ExpectedSymbol(Location),
ExpectedList(Location),
UnknownKeyword(String, Location),
ExpectedOneOf(Vec<String>, String, Location),
TrailingTokens(Location),
UnknownBind(String, Location),
ExpectedType(Location),
}
fn expect_symbol(ast: Option<Ast>, loc: &Location) -> Result<Localised<String>, CompilerError> {
match ast {
Some(Ast::Symbol(s)) => Ok(s),
Some(l) => Err(CompilerError::ExpectedSymbol(l.location().clone())),
None => Err(CompilerError::ExpectedSymbol(loc.tail())),
}
}
fn expect_list(ast: Option<Ast>, loc: &Location) -> Result<Localised<Vec<Ast>>, CompilerError> {
match ast {
Ast::Root(root) => {
for node in root {
match node {
Ast::List(list) => {
let keyword = list[0].clone();
match keyword {
Ast::Symbol(sym) => {
match sym.as_str() {
"module" => {
let cap = list[1].clone();
let exec = list[2].clone();
let memory = list[3].clone();
module.memory_model = Memory {
addressing_model: AddressingModel::Logical,
memory_model: MemoryModel::GLSL450,
};
module.capabilities.push(Capability::Shader);
assert_eq!(exec.symbol(), Some("Logical".to_string()));
assert_eq!(memory.symbol(), Some("GLSL450".to_string()));
assert_eq!(cap.symbol(), Some("Shader".to_string()));
}
"import" => {
let name = match &list[1] {
Ast::Symbol(s) => s,
_ => panic!("Expected symbol! {:?}", list[1]),
};
let value = match &list[2] {
Ast::Symbol(s) => s,
_ => panic!("Expected symbol! {:?}", list[2]),
};
module.imports.push(Import {
name: name.to_string(),
value: value.to_string(),
});
}
"bind" => {
let name_and_type = match &list[1] {
Ast::List(l) => l,
_ => panic!("Expected list! {:?}", list[1]),
};
let name_and_type: String = name_and_type
.iter()
.map(|x| match x {
Ast::Symbol(s) => s.to_string(),
_ => panic!("Expected symbol! {:?}", x),
})
.collect();
// name_and_type is of the name:type format, like foo:f32
let name: String =
name_and_type.split(":").collect::<Vec<&str>>()[0]
.to_string();
let typ: String =
name_and_type.split(":").collect::<Vec<&str>>()[1]
.to_string();
let bind = match &list[2] {
Ast::List(l) => l,
_ => panic!("Expected list! {:?}", list[2]),
};
let bind: Vec<String> = bind
.iter()
.map(|x| match x {
Ast::Symbol(s) => s.to_string(),
_ => panic!("Expected symbol! {:?}", x),
})
.collect();
let bind_name = match bind[0].as_str() {
"BuiltIn" => {
Decoration::BuiltIn(BuiltinDecoration::FragCoord)
}
"Location" => Decoration::Location(
bind[1].parse::<u32>().unwrap(),
),
_ => panic!("Unknown bind! {:?}", bind),
};
let mut exists = false;
for var in &module.globals {
if var.name == name {
exists = true;
}
}
if !exists {
module.globals.push(GlobalVariable {
name: name,
typ: typ,
storage_class: StorageClass::Undefined,
decorations: vec![bind_name],
});
}
}
"dec" => {
let name_and_type = match &list[1] {
Ast::Symbol(s) => s,
_ => panic!("Expected symbol! {:?}", list[1]),
};
let name: String =
name_and_type.split(":").collect::<Vec<&str>>()[0]
.to_string();
let typ: String =
name_and_type.split(":").collect::<Vec<&str>>()[1]
.to_string();
let storage_class = match &list[2] {
Ast::Symbol(s) => s,
_ => panic!("Expected symbol! {:?}", list[2]),
};
let mut exists = false;
for var in &module.globals {
if var.name.as_str() == name.as_str() {
exists = true;
}
}
let storage_class = match storage_class.as_str() {
"Input" => StorageClass::Input,
"Output" => StorageClass::Output,
_ => StorageClass::Undefined,
};
if !exists {
module.globals.push(GlobalVariable {
name: name.to_string(),
typ: typ.to_string(),
storage_class: storage_class.clone(),
decorations: vec![],
});
}
if exists {
module
.globals
.iter_mut()
.find(|x| x.name.as_str() == name.as_str())
.unwrap()
.storage_class = storage_class.clone();
}
}
"entry" => {
let name = match &list[1] {
Ast::Symbol(s) => s,
_ => panic!("Expected symbol! {:?}", list[1]),
};
let exec_model = match &list[2] {
Ast::Symbol(s) => s,
_ => panic!("Expected symbol! {:?}", list[2]),
};
let exec_mode = match &list[3] {
Ast::Symbol(s) => s,
_ => panic!("Expected symbol! {:?}", list[3]),
};
let interface = match &list[4] {
Ast::List(l) => l,
_ => panic!("Expected list! {:?}", list[4]),
};
let interface: Vec<String> = interface
.iter()
.map(|x| match x {
Ast::Symbol(s) => s.to_string().replace(":", ""),
_ => panic!("Expected symbol! {:?}", x),
})
.collect();
module.entry_points.push(EntryPoint {
execution_model: ExecutionModel::Fragment,
execution_mode: ExecutionMode::OriginUpperLeft,
name: name.to_string(),
interface: interface,
});
assert_eq!(**exec_model, "Fragment");
assert_eq!(**exec_mode, "OriginUpperLeft");
}
"fun" => {
let name = match &list[1] {
Ast::List(l) => l[0].clone(),
_ => panic!("Expected list! {:?}", list[1]),
};
let name = match name {
Ast::Symbol(s) => s,
_ => panic!("Expected symbol! {:?}", name),
};
let body = list[2..].to_vec();
let location = if let (Some(s), Some(e)) = (
body.first().map(|a| a.location()).flatten(),
body.last().map(|a| a.location()).flatten(),
) {
Location::range(s, e)
} else {
Location::Char { line: 0, col: 0 }
};
let fun = Function {
name: name.to_string(),
return_type: "void".to_string(),
arguments: vec![],
body: Some(vec![]),
ast: Some(Ast::List(Localised {
location,
item: body,
})),
};
module.functions.push(fun);
}
_ => panic!("Unknown keyword! {:?}", sym),
}
}
_ => panic!("List where a keyword was expected! {:?}", keyword),
}
}
_ => panic!("Top-level symbol! {:?}", node),
Some(Ast::List(l)) => Ok(l),
Some(l) => Err(CompilerError::ExpectedList(l.location().clone())),
None => Err(CompilerError::ExpectedList(loc.tail())),
}
}
fn expect_one_of<T>(
options: &[T],
Localised { location, item }: Localised<String>,
) -> Result<Localised<String>, CompilerError>
where
T: PartialEq<String> + Into<String> + Clone,
{
if options.iter().find(|e| **e == item).is_some() {
Ok(Localised { location, item })
} else {
Err(CompilerError::ExpectedOneOf(
options
.iter()
.map(|t| Into::<String>::into(t.clone()))
.collect(),
item,
location,
))
}
}
fn expect_empty_ast<I: Iterator<Item = Ast>>(mut iter: I) -> Result<(), CompilerError> {
match iter.next() {
Some(a) => Err(CompilerError::TrailingTokens(a.location().clone())),
None => Ok(()),
}
}
fn expect_empty<T, I: Iterator<Item = T>>(
mut iter: I,
location: Location,
) -> Result<(), CompilerError> {
match iter.next() {
Some(_) => Err(CompilerError::TrailingTokens(location)),
None => Ok(()),
}
}
pub fn meta_compile(ast: Ast) -> Result<Module, CompilerError> {
let mut module: Module = Module::default();
let Ast::Root(root) = ast else {
panic!("Non-root ast"); // compiler error, fine to panic
};
for node in root {
let Localised {
location,
item: list,
} = expect_list(Some(node), &Location::All)?;
let mut iter = list.into_iter();
match iter.next() {
Some(Ast::Symbol(sym)) => match sym.as_str() {
"module" => {
compile_module(&mut module, iter, location)?;
}
"import" => {
compile_import(&mut module, iter, location)?;
}
"bind" => {
compile_bind(&mut module, iter, location)?;
}
"dec" => {
compile_dec(&mut module, iter, location)?;
}
"entry" => {
compile_entry(&mut module, iter, location)?;
}
"fun" => {
compile_fun(&mut module, iter, location)?;
}
_ => {
let Localised { location, item } = sym;
return Err(CompilerError::UnknownKeyword(item, location));
}
}
},
Some(l) => return Err(CompilerError::ExpectedSymbol(l.location().clone())),
None => return Err(CompilerError::ExpectedSymbol(location)),
}
_ => panic!("Non-root ast"),
}
module
Ok(module)
}

@ -1,4 +1,7 @@
use crate::parser::{parse, tokenize};
use crate::{
compiler::{CompilerError, meta_compile},
parser::{Location, parse, tokenize},
};
#[test]
fn test_compile() {
@ -20,7 +23,7 @@ fn test_compile() {
";
let ast = parse(tokenize(src));
println!("{:#?}", ast);
let module = meta_compile(&mut ast.unwrap());
let module = meta_compile(ast.unwrap()).unwrap();
println!("{:#?}", module);
let test_module = Module {
capabilities: vec![Capability::Shader],
@ -93,3 +96,198 @@ fn test_compile() {
dbg!(&test_module);
assert_eq!(module, test_module);
}
#[test]
fn expected_symbol() {
let src = "
(module Shader Logical )
(import :std GLSL.std.450)
(bind (frag-coord:*v4f32i) (BuiltIn FragCoord))
(bind (out-color:*v4f32o) (Location 0))
(dec frag-coord:*v4f32i Input)
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:frag-coord :out-color))
(fun (main)
(store-ptr (out-color)
(v4f32i (/ (.xy (load-ptr frag-coord))
(v2f32 1920.0 1080.0))
1.0
1.0)))
";
let ast = parse(tokenize(src));
println!("{:#?}", ast);
let module = meta_compile(ast.unwrap());
assert!(matches!(
module,
Err(CompilerError::ExpectedSymbol(Location::Char {
line: 2,
col: 24
}))
))
}
#[test]
fn expected_list() {
let src = "
(module Shader Logical GLSL450)
(import :std GLSL.std.450)
(bind (frag-coord:*v4f32i) (BuiltIn FragCoord))
(bind (out-color:*v4f32o) (Location 0))
(dec frag-coord:*v4f32i Input)
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:frag-coord :out-color))
(fun main
(store-ptr (out-color)
(v4f32i (/ (.xy (load-ptr frag-coord))
(v2f32 1920.0 1080.0))
1.0
1.0)))
";
let ast = parse(tokenize(src));
println!("{:#?}", ast);
let module = meta_compile(ast.unwrap());
let Err(CompilerError::ExpectedList(Location::String { lines, cols })) = module else {
panic!()
};
assert_eq!(lines, 9..=9);
assert_eq!(cols, 6..=9);
}
#[test]
fn unknown_keyword() {
let src = "
(module Shader Logical GLSL450)
(import :std GLSL.std.450)
(bind (frag-coord:*v4f32i) (BuiltIn FragCoord))
(bind (out-color:*v4f32o) (Location 0))
(dec frag-coord:*v4f32i Input)
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:frag-coord :out-color))
(fum (main)
(store-ptr (out-color)
(v4f32i (/ (.xy (load-ptr frag-coord))
(v2f32 1920.0 1080.0))
1.0
1.0)))
";
let ast = parse(tokenize(src));
println!("{:#?}", ast);
let module = meta_compile(ast.unwrap());
let Err(CompilerError::UnknownKeyword(kw, Location::String { lines, cols })) = module else {
panic!()
};
assert_eq!(kw, "fum".to_string());
assert_eq!(lines, 9..=9);
assert_eq!(cols, 2..=4);
}
#[test]
fn expected_one_of() {
let src = "
(module Shader Loical GLSL450)
(import :std GLSL.std.450)
(bind (frag-coord:*v4f32i) (BuiltIn FragCoord))
(bind (out-color:*v4f32o) (Location 0))
(dec frag-coord:*v4f32i Input)
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:frag-coord :out-color))
(fun (main)
(store-ptr (out-color)
(v4f32i (/ (.xy (load-ptr frag-coord))
(v2f32 1920.0 1080.0))
1.0
1.0)))
";
let ast = parse(tokenize(src));
println!("{:#?}", ast);
let module = meta_compile(ast.unwrap());
let Err(CompilerError::ExpectedOneOf(l, kw, Location::String { lines, cols })) = module else {
panic!()
};
assert_eq!(l, vec!["Logical".to_string()]);
assert_eq!(kw, "Loical".to_string());
assert_eq!(lines, 2..=2);
assert_eq!(cols, 16..=21);
}
#[test]
fn trailing_tokens() {
let src = "
(module Shader Logical GLSL450 GLSL451)
(import :std GLSL.std.450)
(bind (frag-coord:*v4f32i) (BuiltIn FragCoord))
(bind (out-color:*v4f32o) (Location 0))
(dec frag-coord:*v4f32i Input)
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:frag-coord :out-color))
(fun (main)
(store-ptr (out-color)
(v4f32i (/ (.xy (load-ptr frag-coord))
(v2f32 1920.0 1080.0))
1.0
1.0)))
";
let ast = parse(tokenize(src));
println!("{:#?}", ast);
let module = meta_compile(ast.unwrap());
let Err(CompilerError::TrailingTokens(Location::String { lines, cols })) = module else {
panic!()
};
assert_eq!(lines, 2..=2);
assert_eq!(cols, 32..=38);
}
#[test]
fn unknown_bind() {
let src = "
(module Shader Logical GLSL450)
(import :std GLSL.std.450)
(bind (frag-coord:*v4f32i) (BuiltIm FragCoord))
(bind (out-color:*v4f32o) (Location 0))
(dec frag-coord:*v4f32i Input)
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:frag-coord :out-color))
(fun (main)
(store-ptr (out-color)
(v4f32i (/ (.xy (load-ptr frag-coord))
(v2f32 1920.0 1080.0))
1.0
1.0)))
";
let ast = parse(tokenize(src));
println!("{:#?}", ast);
let module = meta_compile(ast.unwrap());
dbg!(&module);
let Err(CompilerError::UnknownBind(b, Location::String { lines, cols })) = module else {
panic!()
};
assert_eq!(b, "BuiltIm");
assert_eq!(lines, 4..=4);
assert_eq!(cols, 28..=46);
}
#[test]
fn expected_type() {
let src = "
(module Shader Logical GLSL450)
(import :std GLSL.std.450)
(bind (frag-coord) (BuiltIn FragCoord))
(bind (out-color:*v4f32o) (Location 0))
(dec frag-coord:*v4f32i Input)
(dec out-color:*v4f32o Output)
(entry main Fragment OriginUpperLeft (:frag-coord :out-color))
(fun (main)
(store-ptr (out-color)
(v4f32i (/ (.xy (load-ptr frag-coord))
(v2f32 1920.0 1080.0))
1.0
1.0)))
";
let ast = parse(tokenize(src));
println!("{:#?}", ast);
let module = meta_compile(ast.unwrap());
dbg!(&module);
let Err(CompilerError::ExpectedType(Location::Char { line: 4, col: 18 })) = module else {
panic!()
};
}

@ -3,6 +3,7 @@ mod tests;
use std::iter::Peekable;
use std::ops::{Deref, RangeInclusive};
use std::usize;
use std::vec::IntoIter;
#[derive(Clone, Debug)]
@ -15,6 +16,7 @@ pub enum Location {
lines: RangeInclusive<usize>,
cols: RangeInclusive<usize>,
},
All,
}
#[derive(Debug)]
@ -42,6 +44,7 @@ impl Location {
match self {
Location::Char { line, .. } => *line,
Location::String { lines, .. } => *lines.start(),
Location::All => 0,
}
}
@ -49,6 +52,7 @@ impl Location {
match self {
Location::Char { col, .. } => *col,
Location::String { cols, .. } => *cols.start(),
Location::All => 0,
}
}
@ -56,6 +60,7 @@ impl Location {
match self {
Location::Char { line, .. } => *line,
Location::String { lines, .. } => *lines.end(),
Location::All => usize::MAX,
}
}
@ -63,6 +68,24 @@ impl Location {
match self {
Location::Char { col, .. } => *col,
Location::String { cols, .. } => *cols.end(),
Location::All => usize::MAX,
}
}
pub fn tail(&self) -> Self {
match self {
Location::Char { line, col } => Location::Char {
line: *line,
col: *col,
},
Location::String { lines, cols } => Location::Char {
line: *lines.end(),
col: *cols.end(),
},
Location::All => Location::Char {
line: usize::MAX,
col: usize::MAX,
},
}
}
}
@ -73,6 +96,12 @@ pub struct Localised<T: Clone> {
pub item: T,
}
impl<T: Clone> Localised<T> {
pub fn into_inner(self) -> T {
self.item
}
}
impl<T: Clone> Localised<T> {
#[cfg(test)]
pub fn dummy_location(item: T) -> Self {
@ -119,11 +148,11 @@ impl Ast {
}
}
pub fn location(&self) -> Option<&Location> {
pub fn location(&self) -> &Location {
match self {
Ast::Symbol(Localised { location, .. }) => Some(location),
Ast::List(Localised { location, .. }) => Some(location),
Ast::Root(_) => None,
Ast::Symbol(Localised { location, .. }) => location,
Ast::List(Localised { location, .. }) => location,
Ast::Root(_) => &Location::All,
}
}
}

Loading…
Cancel
Save