Skip to main content

bind/compiler/
symbol_table.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::compiler::{CompilerError, dependency_graph};
6use crate::parser::common::{CompoundIdentifier, Include};
7use crate::parser::{self, bind_library};
8use crate::{linter, make_identifier};
9use std::collections::HashMap;
10use std::fmt;
11use std::ops::Deref;
12
13pub type SymbolTable = HashMap<CompoundIdentifier, Symbol>;
14
15#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
16pub enum Symbol {
17    DeprecatedKey(u32),
18    Key(String, bind_library::ValueType),
19    NumberValue(u64),
20    StringValue(String),
21    BoolValue(bool),
22    EnumValue(String),
23}
24
25impl fmt::Display for Symbol {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        match self {
28            Symbol::DeprecatedKey(key) => write!(f, "DeprecatedKey({})", key),
29            Symbol::Key(key, _) => write!(f, "Key({})", key),
30            Symbol::NumberValue(value) => write!(f, "{}", value),
31            Symbol::StringValue(value) => write!(f, "\"{}\"", value),
32            Symbol::BoolValue(value) => write!(f, "{}", value),
33            Symbol::EnumValue(value) => write!(f, "Enum({})", value),
34        }
35    }
36}
37
38// This struct contains bind library declaration data that's used for inserting symbol
39// table entries.
40struct SymbolTableDeclaration {
41    pub declaration: bind_library::Declaration,
42
43    // Key-value qualified identifiers that are namespaced to the highest dependency that
44    // defined the declaration.
45    pub qualified_k: CompoundIdentifier,
46    pub qualified_v: CompoundIdentifier,
47
48    // Key-value qualified identifiers that are namespaced to the library that defined the
49    // declaration.
50    pub local_qualified_k: CompoundIdentifier,
51    pub local_qualified_v: CompoundIdentifier,
52}
53
54pub fn get_symbol_table_from_libraries<'a>(
55    using: &Vec<Include>,
56    libraries: &[String],
57    lint: bool,
58) -> Result<SymbolTable, CompilerError> {
59    let library_asts: Vec<bind_library::Ast> = libraries
60        .iter()
61        .map(|lib| {
62            let ast = bind_library::Ast::try_from(lib.as_str())
63                .map_err(CompilerError::BindParserError)?;
64            if lint {
65                linter::lint_library(&ast).map_err(CompilerError::LinterError)?;
66            }
67            Ok(ast)
68        })
69        .collect::<Result<_, CompilerError>>()?;
70
71    let dependencies = resolve_dependencies(using, library_asts.iter())?;
72    let aliases = get_aliases(using);
73    construct_symbol_table(dependencies.into_iter(), aliases)
74}
75
76fn get_aliases(using: &Vec<Include>) -> HashMap<CompoundIdentifier, String> {
77    return using
78        .iter()
79        .filter_map(|using| match &using.alias {
80            Some(alias) => Some((using.name.clone(), alias.clone())),
81            None => None,
82        })
83        .collect::<HashMap<_, _>>();
84}
85
86pub fn resolve_dependencies<'a>(
87    using: &Vec<Include>,
88    libraries: impl Iterator<Item = &'a bind_library::Ast> + Clone,
89) -> Result<Vec<&'a bind_library::Ast>, CompilerError> {
90    (|| {
91        let mut graph = dependency_graph::DependencyGraph::new();
92
93        for library in libraries.clone() {
94            graph.insert_node(library.name.clone(), library);
95        }
96
97        for Include { name, .. } in using {
98            graph.insert_edge_from_root(name)?;
99        }
100
101        for library in libraries.clone() {
102            graph.insert_edge_from_root(&library.name)?;
103        }
104
105        for from in libraries {
106            for to in &from.using {
107                graph.insert_edge(&from.name, &to.name)?;
108            }
109        }
110
111        graph.resolve()
112    })()
113    .map_err(CompilerError::DependencyError)
114}
115
116/// Find the namespace of a qualified identifier from the library's includes. Or, if the identifier
117/// is unqualified, return the local qualified identifier.
118fn find_qualified_identifier(
119    declaration: &bind_library::Declaration,
120    using: &Vec<parser::common::Include>,
121    library_names: &std::collections::HashSet<CompoundIdentifier>,
122    local_qualified: &CompoundIdentifier,
123) -> Result<CompoundIdentifier, CompilerError> {
124    if let Some(namespace) = declaration.identifier.parent() {
125        // A declaration of a qualified (i.e. non-local) key must be an extension.
126        if !declaration.extends {
127            return Err(CompilerError::MissingExtendsKeyword(declaration.identifier.clone()));
128        }
129
130        // Special case for deprecated symbols (currently in the fuchsia namespace), return the
131        // declaration as-is.
132        if namespace == make_identifier!["fuchsia"] {
133            return Ok(declaration.identifier.clone());
134        }
135
136        // Find the fully qualified name from the included libraries.
137        let include = using.iter().find(|include| {
138            namespace == include.name || Some(namespace.to_string()) == include.alias
139        });
140
141        if let Some(include) = include {
142            return Ok(include.name.nest(declaration.identifier.name.clone()));
143        }
144
145        // Check if the namespace exists in the compiled library names
146        if library_names.contains(&namespace) {
147            return Ok(namespace.nest(declaration.identifier.name.clone()));
148        }
149
150        return Err(CompilerError::UnresolvedQualification(declaration.identifier.clone()));
151    }
152
153    // It is not valid to extend an unqualified (i.e. local) key.
154    if declaration.extends {
155        return Err(CompilerError::InvalidExtendsKeyword(local_qualified.clone()));
156    }
157
158    // An unqualified/local key is scoped to the current library.
159    Ok(local_qualified.clone())
160}
161
162// Insert entries from |declaration_data| into |symbol_table|.
163fn insert_symbol_entries(
164    symbol_table: &mut SymbolTable,
165    declaration_data: SymbolTableDeclaration,
166) -> Result<(), CompilerError> {
167    let SymbolTableDeclaration {
168        declaration,
169        qualified_k,
170        qualified_v,
171        local_qualified_k,
172        local_qualified_v,
173    } = declaration_data;
174
175    // Type-check the qualified name against the existing symbols, and check that extended
176    // keys are previously defined and that non-extended keys are not.
177    match symbol_table.get(&qualified_k) {
178        Some(Symbol::Key(_, value_type)) => {
179            if !declaration.extends {
180                return Err(CompilerError::DuplicateIdentifier(qualified_k));
181            }
182            if declaration.value_type != *value_type {
183                return Err(CompilerError::TypeMismatch(qualified_k));
184            }
185        }
186        Some(Symbol::DeprecatedKey(_)) => (),
187        Some(_) => {
188            return Err(CompilerError::TypeMismatch(qualified_k));
189        }
190        None => {
191            if declaration.extends {
192                return Err(CompilerError::UndeclaredKey(qualified_k));
193            }
194            symbol_table
195                .insert(qualified_k, Symbol::Key(qualified_v.to_string(), declaration.value_type));
196        }
197    }
198
199    // Insert each value associated with the declaration into the symbol table, taking care
200    // to scope each identifier under the locally qualified identifier of the key. We don't
201    // need to type-check values here since the parser has already done that.
202    for value in &declaration.values {
203        let qualified_value_k = local_qualified_k.nest(value.identifier().to_string());
204        let qualified_value_v = local_qualified_v.nest(value.identifier().to_string());
205        if symbol_table.contains_key(&qualified_value_k) {
206            return Err(CompilerError::DuplicateIdentifier(qualified_value_k));
207        }
208
209        let value_symbol = match value {
210            bind_library::Value::Number(_, value) => Symbol::NumberValue(*value),
211            bind_library::Value::Str(_, value) => Symbol::StringValue(value.clone()),
212            bind_library::Value::Bool(_, value) => Symbol::BoolValue(*value),
213            bind_library::Value::Enum(_) => Symbol::EnumValue(qualified_value_v.to_string()),
214        };
215        symbol_table.insert(qualified_value_k, value_symbol);
216    }
217    Ok(())
218}
219
220/// Construct a map of every key and value defined by `libraries`. The identifiers in the symbol
221/// table will be fully qualified, i.e. they will contain their full namespace. A symbol is
222/// namespaced according to the name of the library it is defined in. If a library defines a value
223/// by extending a previously defined key, then that value will be namespaced to the current library
224/// and not the library of its key.
225pub fn construct_symbol_table(
226    libraries: impl Iterator<Item = impl Deref<Target = bind_library::Ast>>,
227    aliases: HashMap<CompoundIdentifier, String>,
228) -> Result<SymbolTable, CompilerError> {
229    let mut symbol_table: HashMap<CompoundIdentifier, Symbol> = get_deprecated_symbols();
230
231    let libs = libraries.collect::<Vec<_>>();
232    let library_names =
233        libs.iter().map(|lib| lib.name.clone()).collect::<std::collections::HashSet<_>>();
234
235    // Cache extended declarations and insert them into the symbol table after we resolve all
236    // library declarations.
237    let mut extended_declarations: Vec<SymbolTableDeclaration> = vec![];
238
239    for lib in libs {
240        let bind_library::Ast { name, using, declarations } = &*lib;
241
242        let aliased_name = match aliases.get(name) {
243            Some(alias) => Some(make_identifier!(alias)),
244            None => None,
245        };
246
247        for declaration in declarations {
248            // Construct a qualified identifier for this key that's namespaced to the current
249            // library, discarding any other qualifiers. This identifier is used to scope values
250            // defined under this key. We have separate entries for symbol table keys (k) and
251            // values (v) because the key might be aliased.
252            let local_qualified_id = name.nest(declaration.identifier.name.clone());
253
254            // Attempt to match the namespace of the key to an include of the current library, or if
255            // it is unqualified use the local qualified name. Also do a first pass at checking
256            // whether the extend keyword is used correctly. Once again keep separate entries for
257            // symbol table keys (k) and values (v) because the key might be aliased.
258            let qualified_id =
259                find_qualified_identifier(declaration, using, &library_names, &local_qualified_id)?;
260
261            if let Some(alias) = aliased_name.as_ref() {
262                let alias_local_qualified_id = alias.nest(declaration.identifier.name.clone());
263                let alias_qualified_id = find_qualified_identifier(
264                    declaration,
265                    using,
266                    &library_names,
267                    &alias_local_qualified_id,
268                )?;
269
270                let entry_data = SymbolTableDeclaration {
271                    declaration: declaration.clone(),
272                    qualified_k: alias_qualified_id,
273                    qualified_v: qualified_id.clone(),
274                    local_qualified_k: alias_local_qualified_id,
275                    local_qualified_v: local_qualified_id.clone(),
276                };
277
278                if declaration.extends {
279                    extended_declarations.push(entry_data);
280                } else {
281                    insert_symbol_entries(&mut symbol_table, entry_data)?;
282                }
283            }
284
285            let entry_data = SymbolTableDeclaration {
286                declaration: declaration.clone(),
287                qualified_k: qualified_id.clone(),
288                qualified_v: qualified_id,
289                local_qualified_k: local_qualified_id.clone(),
290                local_qualified_v: local_qualified_id,
291            };
292
293            if declaration.extends {
294                extended_declarations.push(entry_data);
295            } else {
296                insert_symbol_entries(&mut symbol_table, entry_data)?;
297            }
298        }
299    }
300
301    for declaration in extended_declarations.into_iter() {
302        insert_symbol_entries(&mut symbol_table, declaration)?;
303    }
304
305    Ok(symbol_table)
306}
307
308#[allow(clippy::vec_init_then_push, reason = "mass allow for https://fxbug.dev/381896734")]
309/// Hard code these symbols during the migration from macros to bind rules. Eventually these
310/// will be defined in libraries and the compiler will emit strings for them in the bytecode.
311fn deprecated_keys() -> Vec<(String, u32)> {
312    let mut keys = Vec::new();
313
314    keys.push(("BIND_PROTOCOL".to_string(), 0x0001));
315
316    keys.push(("BIND_AUTOBIND".to_string(), 0x0002));
317
318    keys.push(("BIND_COMPOSITE".to_string(), 0x0003));
319
320    keys.push(("BIND_PLATFORM_DEV_VID".to_string(), 0x0300));
321    keys.push(("BIND_PCI_VID".to_string(), 0x0100));
322
323    keys.push(("BIND_PCI_DID".to_string(), 0x0101));
324    keys.push(("BIND_PCI_CLASS".to_string(), 0x0102));
325    keys.push(("BIND_PCI_SUBCLASS".to_string(), 0x0103));
326    keys.push(("BIND_PCI_INTERFACE".to_string(), 0x0104));
327    keys.push(("BIND_PCI_REVISION".to_string(), 0x0105));
328    keys.push(("BIND_PCI_TOPO".to_string(), 0x0107));
329
330    // usb binding variables at 0x02XX
331    // these are used for both ZX_PROTOCOL_USB_INTERFACE and ZX_PROTOCOL_USB_FUNCTION
332    keys.push(("BIND_USB_VID".to_string(), 0x0200));
333    keys.push(("BIND_USB_PID".to_string(), 0x0201));
334    keys.push(("BIND_USB_CLASS".to_string(), 0x0202));
335    keys.push(("BIND_USB_SUBCLASS".to_string(), 0x0203));
336    keys.push(("BIND_USB_PROTOCOL".to_string(), 0x0204));
337    keys.push(("BIND_USB_INTERFACE_NUMBER".to_string(), 0x0205));
338
339    // Platform bus binding variables at 0x03XX
340    keys.push(("BIND_PLATFORM_DEV_VID".to_string(), 0x0300));
341    keys.push(("BIND_PLATFORM_DEV_PID".to_string(), 0x0301));
342    keys.push(("BIND_PLATFORM_DEV_DID".to_string(), 0x0302));
343    keys.push(("BIND_PLATFORM_DEV_INSTANCE_ID".to_string(), 0x0304));
344    keys.push(("BIND_PLATFORM_DEV_INTERRUPT_ID".to_string(), 0x0305));
345
346    // ACPI binding variables at 0x04XX
347    keys.push(("BIND_ACPI_BUS_TYPE".to_string(), 0x0400));
348    keys.push(("BIND_ACPI_ID".to_string(), 0x0401));
349
350    // Intel HDA Codec binding variables at 0x05XX
351    keys.push(("BIND_IHDA_CODEC_VID".to_string(), 0x0500));
352    keys.push(("BIND_IHDA_CODEC_DID".to_string(), 0x0501));
353
354    // Serial binding variables at 0x06XX
355    keys.push(("BIND_SERIAL_CLASS".to_string(), 0x0600));
356
357    // NAND binding variables at 0x07XX
358    keys.push(("BIND_NAND_CLASS".to_string(), 0x0700));
359
360    // SDIO binding variables at 0x09XX
361    keys.push(("BIND_SDIO_VID".to_string(), 0x0900));
362    keys.push(("BIND_SDIO_PID".to_string(), 0x0901));
363    keys.push(("BIND_SDIO_FUNCTION".to_string(), 0x0902));
364
365    // Init step binding variables at 0x0A6X.
366    keys.push(("BIND_INIT_STEP".to_string(), 0x0A60));
367
368    keys
369}
370
371fn get_deprecated_symbols() -> SymbolTable {
372    let mut symbol_table = HashMap::new();
373    for (key, value) in deprecated_keys() {
374        symbol_table.insert(make_identifier!("fuchsia", key), Symbol::DeprecatedKey(value));
375    }
376    symbol_table
377}
378
379pub fn get_deprecated_key_identifiers() -> HashMap<u32, String> {
380    let mut key_identifiers = HashMap::new();
381    for (key, value) in deprecated_keys() {
382        key_identifiers.insert(value, make_identifier!("fuchsia", key).to_string());
383    }
384    key_identifiers
385}
386
387pub fn get_deprecated_key_identifier(key: u32) -> Option<String> {
388    match key {
389        0x0001 => Some("fuchsia.BIND_PROTOCOL".to_string()),
390        0x0002 => Some("fuchsia.BIND_AUTOBIND".to_string()),
391        0x0003 => Some("fuchsia.BIND_COMPOSITE".to_string()),
392
393        // PCI binding variables at 0x01XX.
394        0x0100 => Some("fuchsia.BIND_PCI_VID".to_string()),
395        0x0101 => Some("fuchsia.BIND_PCI_DID".to_string()),
396        0x0102 => Some("fuchsia.BIND_PCI_CLASS".to_string()),
397        0x0103 => Some("fuchsia.BIND_PCI_SUBCLASS".to_string()),
398        0x0104 => Some("fuchsia.BIND_PCI_INTERFACE".to_string()),
399        0x0105 => Some("fuchsia.BIND_PCI_REVISION".to_string()),
400        0x0107 => Some("fuchsia.BIND_PCI_TOPO".to_string()),
401
402        // USB binding variables at 0x02XX.
403        0x0200 => Some("fuchsia.BIND_USB_VID".to_string()),
404        0x0201 => Some("fuchsia.BIND_USB_PID".to_string()),
405        0x0202 => Some("fuchsia.BIND_USB_CLASS".to_string()),
406        0x0203 => Some("fuchsia.BIND_USB_SUBCLASS".to_string()),
407        0x0204 => Some("fuchsia.BIND_USB_PROTOCOL".to_string()),
408        0x0205 => Some("fuchsia.BIND_USB_INTERFACE_NUMBER".to_string()),
409
410        // Platform bus binding variables at 0x03XX.
411        0x0300 => Some("fuchsia.BIND_PLATFORM_DEV_VID".to_string()),
412        0x0301 => Some("fuchsia.BIND_PLATFORM_DEV_PID".to_string()),
413        0x0302 => Some("fuchsia.BIND_PLATFORM_DEV_DID".to_string()),
414        0x0304 => Some("fuchsia.BIND_PLATFORM_DEV_INSTANCE_ID".to_string()),
415        0x0305 => Some("fuchsia.BIND_PLATFORM_DEV_INTERRUPT_ID".to_string()),
416
417        // ACPI binding variables at 0x04XX.
418        0x0400 => Some("fuchsia.BIND_ACPI_BUS_TYPE".to_string()),
419        0x0401 => Some("fuchsia.BIND_ACPI_ID".to_string()),
420
421        // Intel HDA Codec binding variables at 0x05XX.
422        0x0500 => Some("fuchsia.BIND_IHDA_CODEC_VID".to_string()),
423        0x0501 => Some("fuchsia.BIND_IHDA_CODEC_DID".to_string()),
424
425        // Serial binding variables at 0x06XX.
426        0x0600 => Some("fuchsia.BIND_SERIAL_CLASS".to_string()),
427
428        // NAND binding variables at 0x07XX.
429        0x0700 => Some("fuchsia.BIND_NAND_CLASS".to_string()),
430
431        // SDIO binding variables at 0x09XX.
432        0x0900 => Some("fuchsia.BIND_SDIO_VID".to_string()),
433        0x0901 => Some("fuchsia.BIND_SDIO_PID".to_string()),
434        0x0902 => Some("fuchsia.BIND_SDIO_FUNCTION".to_string()),
435
436        // Init step binding variables at 0x0A6X.
437        0x0A60 => Some("fuchsia.BIND_INIT_STEP".to_string()),
438
439        _ => None,
440    }
441}
442
443pub fn get_deprecated_key_value(key: &str) -> Option<u32> {
444    match key {
445        "fuchsia.BIND_PROTOCOL" => Some(0x0001),
446        "fuchsia.BIND_AUTOBIND" => Some(0x0002),
447        "fuchsia.BIND_COMPOSITE" => Some(0x0003),
448
449        // PCI binding variables at 0x01XX.
450        "fuchsia.BIND_PCI_VID" => Some(0x0100),
451        "fuchsia.BIND_PCI_DID" => Some(0x0101),
452        "fuchsia.BIND_PCI_CLASS" => Some(0x0102),
453        "fuchsia.BIND_PCI_SUBCLASS" => Some(0x0103),
454        "fuchsia.BIND_PCI_INTERFACE" => Some(0x0104),
455        "fuchsia.BIND_PCI_REVISION" => Some(0x0105),
456        "fuchsia.BIND_PCI_TOPO" => Some(0x0107),
457
458        // USB binding variables at 0x02XX.
459        "fuchsia.BIND_USB_VID" => Some(0x0200),
460        "fuchsia.BIND_USB_PID" => Some(0x0201),
461        "fuchsia.BIND_USB_CLASS" => Some(0x0202),
462        "fuchsia.BIND_USB_SUBCLASS" => Some(0x0203),
463        "fuchsia.BIND_USB_PROTOCOL" => Some(0x0204),
464        "fuchsia.BIND_USB_INTERFACE_NUMBER" => Some(0x0205),
465
466        // Platform bus binding variables at 0x03XX
467        "fuchsia.BIND_PLATFORM_DEV_VID" => Some(0x0300),
468        "fuchsia.BIND_PLATFORM_DEV_PID" => Some(0x0301),
469        "fuchsia.BIND_PLATFORM_DEV_DID" => Some(0x0302),
470        "fuchsia.BIND_PLATFORM_DEV_INSTANCE_ID" => Some(0x0304),
471        "fuchsia.BIND_PLATFORM_DEV_INTERRUPT_ID" => Some(0x0305),
472
473        // ACPI binding variables at 0x04XX
474        "fuchsia.BIND_ACPI_BUS_TYPE" => Some(0x0400),
475        "fuchsia.BIND_ACPI_ID" => Some(0x0401),
476
477        // Intel HDA Codec binding variables at 0x05XX
478        "fuchsia.BIND_IHDA_CODEC_VID" => Some(0x0500),
479        "fuchsia.BIND_IHDA_CODEC_DID" => Some(0x0501),
480
481        // Serial binding variables at 0x06XX
482        "fuchsia.BIND_SERIAL_CLASS" => Some(0x0600),
483
484        // NAND binding variables at 0x07XX
485        "fuchsia.BIND_NAND_CLASS" => Some(0x0700),
486
487        // SDIO binding variables at 0x09XX
488        "fuchsia.BIND_SDIO_VID" => Some(0x0900),
489        "fuchsia.BIND_SDIO_PID" => Some(0x0901),
490        "fuchsia.BIND_SDIO_FUNCTION" => Some(0x0902),
491
492        // Init step binding variables at 0x0A6X.
493        "fuchsia.BIND_INIT_STEP" => Some(0x0A60),
494
495        _ => None,
496    }
497}
498
499#[cfg(test)]
500mod test {
501    use super::*;
502    use crate::make_identifier;
503    use crate::parser::bind_library;
504    use crate::parser::common::Include;
505
506    mod symbol_table {
507        use super::*;
508
509        #[test]
510        fn simple_key_and_value() {
511            let libraries = vec![bind_library::Ast {
512                name: make_identifier!("test"),
513                using: vec![],
514                declarations: vec![bind_library::Declaration {
515                    identifier: make_identifier!["symbol"],
516                    value_type: bind_library::ValueType::Number,
517                    extends: false,
518                    values: vec![(bind_library::Value::Number("x".to_string(), 1))],
519                }],
520            }];
521
522            let st = construct_symbol_table(libraries.iter(), HashMap::new()).unwrap();
523            assert_eq!(
524                st.get(&make_identifier!("test", "symbol")),
525                Some(&Symbol::Key("test.symbol".to_string(), bind_library::ValueType::Number))
526            );
527            assert_eq!(
528                st.get(&make_identifier!("test", "symbol", "x")),
529                Some(&Symbol::NumberValue(1))
530            );
531        }
532
533        #[test]
534        fn all_value_types() {
535            let libraries = vec![bind_library::Ast {
536                name: make_identifier!("hummingbird"),
537                using: vec![],
538                declarations: vec![
539                    bind_library::Declaration {
540                        identifier: make_identifier!["sunbeam"],
541                        value_type: bind_library::ValueType::Number,
542                        extends: false,
543                        values: vec![(bind_library::Value::Number("shining".to_string(), 1))],
544                    },
545                    bind_library::Declaration {
546                        identifier: make_identifier!["mountaingem"],
547                        value_type: bind_library::ValueType::Bool,
548                        extends: false,
549                        values: vec![
550                            (bind_library::Value::Bool("white-bellied".to_string(), false)),
551                        ],
552                    },
553                    bind_library::Declaration {
554                        identifier: make_identifier!["brilliant"],
555                        value_type: bind_library::ValueType::Enum,
556                        extends: false,
557                        values: vec![(bind_library::Value::Enum("black-throated".to_string()))],
558                    },
559                    bind_library::Declaration {
560                        identifier: make_identifier!["woodnymph"],
561                        value_type: bind_library::ValueType::Str,
562                        extends: false,
563                        values: vec![
564                            (bind_library::Value::Str(
565                                "fork-tailed".to_string(),
566                                "sabrewing".to_string(),
567                            )),
568                        ],
569                    },
570                ],
571            }];
572
573            let st = construct_symbol_table(libraries.iter(), HashMap::new()).unwrap();
574            assert_eq!(
575                st.get(&make_identifier!("hummingbird", "sunbeam", "shining")),
576                Some(&Symbol::NumberValue(1))
577            );
578            assert_eq!(
579                st.get(&make_identifier!("hummingbird", "mountaingem", "white-bellied")),
580                Some(&Symbol::BoolValue(false))
581            );
582            assert_eq!(
583                st.get(&make_identifier!("hummingbird", "brilliant", "black-throated")),
584                Some(&Symbol::EnumValue("hummingbird.brilliant.black-throated".to_string()))
585            );
586            assert_eq!(
587                st.get(&make_identifier!("hummingbird", "woodnymph", "fork-tailed")),
588                Some(&Symbol::StringValue("sabrewing".to_string()))
589            );
590        }
591
592        #[test]
593        fn extension() {
594            let libraries = vec![
595                bind_library::Ast {
596                    name: make_identifier!("lib_a"),
597                    using: vec![],
598                    declarations: vec![bind_library::Declaration {
599                        identifier: make_identifier!["symbol"],
600                        value_type: bind_library::ValueType::Number,
601                        extends: false,
602                        values: vec![(bind_library::Value::Number("x".to_string(), 1))],
603                    }],
604                },
605                bind_library::Ast {
606                    name: make_identifier!("lib_b"),
607                    using: vec![Include { name: make_identifier!("lib_a"), alias: None }],
608                    declarations: vec![bind_library::Declaration {
609                        identifier: make_identifier!["lib_a", "symbol"],
610                        value_type: bind_library::ValueType::Number,
611                        extends: true,
612                        values: vec![(bind_library::Value::Number("y".to_string(), 2))],
613                    }],
614                },
615            ];
616
617            let st = construct_symbol_table(libraries.iter(), HashMap::new()).unwrap();
618            assert_eq!(
619                st.get(&make_identifier!("lib_a", "symbol")),
620                Some(&Symbol::Key("lib_a.symbol".to_string(), bind_library::ValueType::Number))
621            );
622            assert_eq!(
623                st.get(&make_identifier!("lib_a", "symbol", "x")),
624                Some(&Symbol::NumberValue(1))
625            );
626            assert_eq!(
627                st.get(&make_identifier!("lib_b", "symbol", "y")),
628                Some(&Symbol::NumberValue(2))
629            );
630        }
631
632        #[test]
633        fn extension_with_dependency_defined_after_library() {
634            let libraries = vec![
635                bind_library::Ast {
636                    name: make_identifier!("lib_b"),
637                    using: vec![Include { name: make_identifier!("lib_a"), alias: None }],
638                    declarations: vec![bind_library::Declaration {
639                        identifier: make_identifier!["lib_a", "symbol"],
640                        value_type: bind_library::ValueType::Number,
641                        extends: true,
642                        values: vec![(bind_library::Value::Number("y".to_string(), 2))],
643                    }],
644                },
645                bind_library::Ast {
646                    name: make_identifier!("lib_a"),
647                    using: vec![],
648                    declarations: vec![bind_library::Declaration {
649                        identifier: make_identifier!["symbol"],
650                        value_type: bind_library::ValueType::Number,
651                        extends: false,
652                        values: vec![(bind_library::Value::Number("x".to_string(), 1))],
653                    }],
654                },
655            ];
656
657            let st = construct_symbol_table(libraries.iter(), HashMap::new()).unwrap();
658            assert_eq!(
659                st.get(&make_identifier!("lib_a", "symbol")),
660                Some(&Symbol::Key("lib_a.symbol".to_string(), bind_library::ValueType::Number))
661            );
662            assert_eq!(
663                st.get(&make_identifier!("lib_a", "symbol", "x")),
664                Some(&Symbol::NumberValue(1))
665            );
666            assert_eq!(
667                st.get(&make_identifier!("lib_b", "symbol", "y")),
668                Some(&Symbol::NumberValue(2))
669            );
670        }
671
672        #[test]
673        fn aliased_extension() {
674            let libraries = vec![
675                bind_library::Ast {
676                    name: make_identifier!("lib_a"),
677                    using: vec![],
678                    declarations: vec![bind_library::Declaration {
679                        identifier: make_identifier!["symbol"],
680                        value_type: bind_library::ValueType::Number,
681                        extends: false,
682                        values: vec![(bind_library::Value::Number("x".to_string(), 1))],
683                    }],
684                },
685                bind_library::Ast {
686                    name: make_identifier!("lib_b"),
687                    using: vec![Include {
688                        name: make_identifier!("lib_a"),
689                        alias: Some("alias".to_string()),
690                    }],
691                    declarations: vec![bind_library::Declaration {
692                        identifier: make_identifier!["alias", "symbol"],
693                        value_type: bind_library::ValueType::Number,
694                        extends: true,
695                        values: vec![(bind_library::Value::Number("y".to_string(), 2))],
696                    }],
697                },
698            ];
699
700            let st = construct_symbol_table(libraries.iter(), HashMap::new()).unwrap();
701            assert_eq!(
702                st.get(&make_identifier!("lib_a", "symbol")),
703                Some(&Symbol::Key("lib_a.symbol".to_string(), bind_library::ValueType::Number))
704            );
705            assert_eq!(
706                st.get(&make_identifier!("lib_a", "symbol", "x")),
707                Some(&Symbol::NumberValue(1))
708            );
709            assert_eq!(
710                st.get(&make_identifier!("lib_b", "symbol", "y")),
711                Some(&Symbol::NumberValue(2))
712            );
713        }
714
715        #[test]
716        fn aliased_extension_with_library_alias() {
717            let libraries = vec![
718                bind_library::Ast {
719                    name: make_identifier!("lib_a"),
720                    using: vec![],
721                    declarations: vec![bind_library::Declaration {
722                        identifier: make_identifier!["symbol"],
723                        value_type: bind_library::ValueType::Number,
724                        extends: false,
725                        values: vec![(bind_library::Value::Number("x".to_string(), 1))],
726                    }],
727                },
728                bind_library::Ast {
729                    name: make_identifier!("lib_b"),
730                    using: vec![Include {
731                        name: make_identifier!("lib_a"),
732                        alias: Some("alias".to_string()),
733                    }],
734                    declarations: vec![
735                        bind_library::Declaration {
736                            identifier: make_identifier!["alias", "symbol"],
737                            value_type: bind_library::ValueType::Number,
738                            extends: true,
739                            values: vec![(bind_library::Value::Number("y".to_string(), 2))],
740                        },
741                        bind_library::Declaration {
742                            identifier: make_identifier!["enum_symbol"],
743                            value_type: bind_library::ValueType::Enum,
744                            extends: false,
745                            values: vec![(bind_library::Value::Enum("the_val".to_string()))],
746                        },
747                    ],
748                },
749            ];
750
751            // The symbol table will be constructed with 'lib_b' aliased as 'opaque'.
752            let st = construct_symbol_table(
753                libraries.iter(),
754                HashMap::from([(make_identifier!("lib_b"), "opaque".to_string())]),
755            )
756            .unwrap();
757
758            assert_eq!(
759                st.get(&make_identifier!("lib_a", "symbol")),
760                Some(&Symbol::Key("lib_a.symbol".to_string(), bind_library::ValueType::Number))
761            );
762            assert_eq!(
763                st.get(&make_identifier!("lib_a", "symbol", "x")),
764                Some(&Symbol::NumberValue(1))
765            );
766            assert_eq!(
767                st.get(&make_identifier!("opaque", "symbol", "y")),
768                Some(&Symbol::NumberValue(2))
769            );
770            assert_eq!(
771                st.get(&make_identifier!("lib_b", "symbol", "y")),
772                Some(&Symbol::NumberValue(2))
773            );
774            assert_eq!(
775                st.get(&make_identifier!("opaque", "enum_symbol")),
776                Some(&Symbol::Key("lib_b.enum_symbol".to_string(), bind_library::ValueType::Enum))
777            );
778            assert_eq!(
779                st.get(&make_identifier!("lib_b", "enum_symbol")),
780                Some(&Symbol::Key("lib_b.enum_symbol".to_string(), bind_library::ValueType::Enum))
781            );
782            assert_eq!(
783                st.get(&make_identifier!("opaque", "enum_symbol", "the_val")),
784                Some(&Symbol::EnumValue("lib_b.enum_symbol.the_val".to_string()))
785            );
786            assert_eq!(
787                st.get(&make_identifier!("lib_b", "enum_symbol", "the_val")),
788                Some(&Symbol::EnumValue("lib_b.enum_symbol.the_val".to_string()))
789            );
790        }
791
792        #[test]
793        fn deprecated_key_extension() {
794            let libraries = vec![bind_library::Ast {
795                name: make_identifier!("lib_a"),
796                using: vec![],
797                declarations: vec![bind_library::Declaration {
798                    identifier: make_identifier!["fuchsia", "BIND_PCI_DID"],
799                    value_type: bind_library::ValueType::Number,
800                    extends: true,
801                    values: vec![(bind_library::Value::Number("x".to_string(), 0x1234))],
802                }],
803            }];
804
805            let st = construct_symbol_table(libraries.iter(), HashMap::new()).unwrap();
806            assert_eq!(
807                st.get(&make_identifier!("lib_a", "BIND_PCI_DID", "x")),
808                Some(&Symbol::NumberValue(0x1234))
809            );
810        }
811
812        #[test]
813        fn duplicate_key() {
814            let libraries = vec![bind_library::Ast {
815                name: make_identifier!("test"),
816                using: vec![],
817                declarations: vec![
818                    bind_library::Declaration {
819                        identifier: make_identifier!["symbol"],
820                        value_type: bind_library::ValueType::Number,
821                        extends: false,
822                        values: vec![],
823                    },
824                    bind_library::Declaration {
825                        identifier: make_identifier!["symbol"],
826                        value_type: bind_library::ValueType::Number,
827                        extends: false,
828                        values: vec![],
829                    },
830                ],
831            }];
832
833            assert_eq!(
834                construct_symbol_table(libraries.iter(), HashMap::new()),
835                Err(CompilerError::DuplicateIdentifier(make_identifier!("test", "symbol")))
836            );
837        }
838
839        #[test]
840        fn duplicate_value() {
841            let libraries = vec![bind_library::Ast {
842                name: make_identifier!("test"),
843                using: vec![],
844                declarations: vec![bind_library::Declaration {
845                    identifier: make_identifier!["symbol"],
846                    value_type: bind_library::ValueType::Number,
847                    extends: false,
848                    values: vec![
849                        bind_library::Value::Number("a".to_string(), 1),
850                        bind_library::Value::Number("a".to_string(), 2),
851                    ],
852                }],
853            }];
854
855            assert_eq!(
856                construct_symbol_table(libraries.iter(), HashMap::new()),
857                Err(CompilerError::DuplicateIdentifier(make_identifier!("test", "symbol", "a")))
858            );
859        }
860
861        #[test]
862        fn keys_are_qualified() {
863            // The same symbol declared in two libraries should not collide.
864            let libraries = vec![
865                bind_library::Ast {
866                    name: make_identifier!("lib_a"),
867                    using: vec![],
868                    declarations: vec![bind_library::Declaration {
869                        identifier: make_identifier!["symbol"],
870                        value_type: bind_library::ValueType::Number,
871                        extends: false,
872                        values: vec![],
873                    }],
874                },
875                bind_library::Ast {
876                    name: make_identifier!("lib_b"),
877                    using: vec![],
878                    declarations: vec![bind_library::Declaration {
879                        identifier: make_identifier!["symbol"],
880                        value_type: bind_library::ValueType::Number,
881                        extends: false,
882                        values: vec![],
883                    }],
884                },
885            ];
886
887            let st = construct_symbol_table(libraries.iter(), HashMap::new()).unwrap();
888            assert_eq!(
889                st.get(&make_identifier!("lib_a", "symbol")),
890                Some(&Symbol::Key("lib_a.symbol".to_string(), bind_library::ValueType::Number))
891            );
892            assert_eq!(
893                st.get(&make_identifier!("lib_b", "symbol")),
894                Some(&Symbol::Key("lib_b.symbol".to_string(), bind_library::ValueType::Number))
895            );
896        }
897
898        #[test]
899        fn missing_extend_keyword() {
900            // A library referring to a previously declared symbol must use the "extend" keyword.
901            let libraries = vec![
902                bind_library::Ast {
903                    name: make_identifier!("lib_a"),
904                    using: vec![],
905                    declarations: vec![bind_library::Declaration {
906                        identifier: make_identifier!["symbol"],
907                        value_type: bind_library::ValueType::Number,
908                        extends: false,
909                        values: vec![],
910                    }],
911                },
912                bind_library::Ast {
913                    name: make_identifier!("lib_b"),
914                    using: vec![],
915                    declarations: vec![bind_library::Declaration {
916                        identifier: make_identifier!["lib_a", "symbol"],
917                        value_type: bind_library::ValueType::Number,
918                        extends: false,
919                        values: vec![],
920                    }],
921                },
922            ];
923
924            assert_eq!(
925                construct_symbol_table(libraries.iter(), HashMap::new()),
926                Err(CompilerError::MissingExtendsKeyword(make_identifier!("lib_a", "symbol")))
927            );
928        }
929
930        #[test]
931        fn invalid_extend_keyword() {
932            // A library cannot declare an unqualified (and therefore locally namespaced) symbol
933            // with the "extend" keyword.
934            let libraries = vec![bind_library::Ast {
935                name: make_identifier!("lib_a"),
936                using: vec![],
937                declarations: vec![bind_library::Declaration {
938                    identifier: make_identifier!["symbol"],
939                    value_type: bind_library::ValueType::Number,
940                    extends: true,
941                    values: vec![],
942                }],
943            }];
944
945            assert_eq!(
946                construct_symbol_table(libraries.iter(), HashMap::new()),
947                Err(CompilerError::InvalidExtendsKeyword(make_identifier!("lib_a", "symbol")))
948            );
949        }
950
951        #[test]
952        fn unresolved_qualification() {
953            // A library cannot refer to a qualified identifier where the qualifier is not in its
954            // list of includes.
955            let libraries = vec![bind_library::Ast {
956                name: make_identifier!("lib_a"),
957                using: vec![],
958                declarations: vec![bind_library::Declaration {
959                    identifier: make_identifier!["lib_b", "symbol"],
960                    value_type: bind_library::ValueType::Number,
961                    extends: true,
962                    values: vec![],
963                }],
964            }];
965
966            assert_eq!(
967                construct_symbol_table(libraries.iter(), HashMap::new()),
968                Err(CompilerError::UnresolvedQualification(make_identifier!("lib_b", "symbol")))
969            );
970        }
971
972        #[test]
973        fn undeclared_key() {
974            let libraries = vec![
975                bind_library::Ast {
976                    name: make_identifier!("lib_a"),
977                    using: vec![],
978                    declarations: vec![],
979                },
980                bind_library::Ast {
981                    name: make_identifier!("lib_b"),
982                    using: vec![Include { name: make_identifier!("lib_a"), alias: None }],
983                    declarations: vec![bind_library::Declaration {
984                        identifier: make_identifier!["lib_a", "symbol"],
985                        value_type: bind_library::ValueType::Number,
986                        extends: true,
987                        values: vec![],
988                    }],
989                },
990            ];
991
992            assert_eq!(
993                construct_symbol_table(libraries.iter(), HashMap::new()),
994                Err(CompilerError::UndeclaredKey(make_identifier!("lib_a", "symbol")))
995            );
996        }
997
998        #[test]
999        fn type_mismatch() {
1000            let libraries = vec![
1001                bind_library::Ast {
1002                    name: make_identifier!("lib_a"),
1003                    using: vec![],
1004                    declarations: vec![bind_library::Declaration {
1005                        identifier: make_identifier!["symbol"],
1006                        value_type: bind_library::ValueType::Str,
1007                        extends: false,
1008                        values: vec![],
1009                    }],
1010                },
1011                bind_library::Ast {
1012                    name: make_identifier!("lib_b"),
1013                    using: vec![Include { name: make_identifier!("lib_a"), alias: None }],
1014                    declarations: vec![bind_library::Declaration {
1015                        identifier: make_identifier!["lib_a", "symbol"],
1016                        value_type: bind_library::ValueType::Number,
1017                        extends: true,
1018                        values: vec![],
1019                    }],
1020                },
1021            ];
1022
1023            assert_eq!(
1024                construct_symbol_table(libraries.iter(), HashMap::new()),
1025                Err(CompilerError::TypeMismatch(make_identifier!("lib_a", "symbol")))
1026            );
1027        }
1028    }
1029}