Debug case statement by supporting other semantics

This commit is contained in:
Guojie Luo 2022-03-22 17:57:37 +08:00
parent 83dfd6e618
commit f3f7113dc5
8 changed files with 269 additions and 119 deletions

View File

@ -84,7 +84,7 @@ fn main_inner() -> Result<(), std::io::Error> {
for (mut module, _json_module) in module_list { for (mut module, _json_module) in module_list {
let pass_ctx = PassContext; let pass_ctx = PassContext;
llhd::pass::GlobalCommonSubexprElim::run_on_module(&pass_ctx, &mut module); //llhd::pass::GlobalCommonSubexprElim::run_on_module(&pass_ctx, &mut module);
//llhd::pass::InstSimplification::run_on_module(&pass_ctx, &mut module); //llhd::pass::InstSimplification::run_on_module(&pass_ctx, &mut module);
llhd::assembly::write_module(std::io::stdout(), &module); llhd::assembly::write_module(std::io::stdout(), &module);
} }

View File

@ -137,8 +137,11 @@ impl CaseStatement {
let json_children = &json_case_item["children"]; let json_children = &json_case_item["children"];
assert_eq!(json_children.len(), 3); assert_eq!(json_children.len(), 3);
let json_case_expressions = let json_case_expressions = Tools::match_tags(
Tools::match_tags(json_children[0].members().collect(), vec![Tag::EXPRESSION]); vec![&json_children[0]],
vec![Tag::EXPRESSION_LIST, Tag::EXPRESSION],
);
assert!(json_case_expressions.len() > 0);
let json_statement = &json_children[2]; let json_statement = &json_children[2];

View File

@ -3,6 +3,7 @@ use linked_hash_set::LinkedHashSet;
use llhd::{ use llhd::{
ir::{Arg, Block, Module, Signature, UnitBuilder, UnitData, UnitKind, UnitName, Value}, ir::{Arg, Block, Module, Signature, UnitBuilder, UnitData, UnitKind, UnitName, Value},
ty::Type, ty::Type,
value::IntValue,
}; };
use std::{collections::HashMap, fmt}; use std::{collections::HashMap, fmt};
@ -10,6 +11,7 @@ pub struct ModuleContext {
pub name: String, pub name: String,
pub module: Module, pub module: Module,
pub symbol: LinkedHashMap<String, SymbolInfo>, pub symbol: LinkedHashMap<String, SymbolInfo>,
pub param: LinkedHashMap<String, IntValue>,
pub unit_ctx: UnitContext, pub unit_ctx: UnitContext,
} }
@ -58,6 +60,7 @@ impl ModuleContext {
name: String::new(), name: String::new(),
module: Module::new(), module: Module::new(),
symbol: LinkedHashMap::new(), symbol: LinkedHashMap::new(),
param: LinkedHashMap::new(),
unit_ctx: UnitContext::new(), unit_ctx: UnitContext::new(),
} }
} }

View File

@ -1,6 +1,11 @@
use crate::cst::{ModuleContext, Tag, Tools, UnitContext}; use crate::cst::{ModuleContext, Tag, Tools, UnitContext};
use json::JsonValue; use json::JsonValue;
use llhd::ir::Value; use llhd::{
ir::{UnitBuilder, Value},
ty::int_ty,
value::IntValue,
};
use num::BigInt;
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
@ -26,9 +31,8 @@ impl Expression {
warn!("TODO: case '{}'", tag); warn!("TODO: case '{}'", tag);
None None
} }
Some(tag @ Tag::CONCATENATION_EXPRESSION) => { Some(Tag::CONCATENATION_EXPRESSION) => {
warn!("TODO: case '{}'", tag); Self::gen_concatenation_expression(json, context)
None
} }
Some(Tag::PAREN_GROUP) => { Some(Tag::PAREN_GROUP) => {
let json_expression = let json_expression =
@ -67,13 +71,8 @@ impl Expression {
let raw_name_to_value = &mut unit_ctx.raw_name_to_value; let raw_name_to_value = &mut unit_ctx.raw_name_to_value;
let raw_name_to_shadow = &unit_ctx.raw_name_to_shadow; let raw_name_to_shadow = &unit_ctx.raw_name_to_shadow;
assert!( if raw_name_to_value.contains_key(&symbol) {
raw_name_to_value.contains_key(&symbol),
"symbol '{}' not found",
symbol
);
let value = raw_name_to_value[&symbol]; let value = raw_name_to_value[&symbol];
let shadow = raw_name_to_shadow.get(&symbol).cloned(); let shadow = raw_name_to_shadow.get(&symbol).cloned();
let mut builder = UnitContext::builder(&mut unit_ctx.data); let mut builder = UnitContext::builder(&mut unit_ctx.data);
@ -84,34 +83,38 @@ impl Expression {
None => builder.ins().prb(value), None => builder.ins().prb(value),
Some(s) => builder.ins().ld(s), Some(s) => builder.ins().ld(s),
}) })
} else if context.param.contains_key(&symbol) {
let mut builder = UnitContext::builder(&mut unit_ctx.data);
let bb_head = *unit_ctx.bb_head.last().unwrap();
builder.append_to(bb_head);
Some(builder.ins().const_int(context.param[&symbol].clone()))
} else {
panic!("unknown error at CST node {}", json);
}
} else { } else {
None None
} }
} }
fn gen_number(json: &JsonValue, context: &mut ModuleContext) -> Option<Value> { pub fn parse_number(json: &JsonValue) -> IntValue {
let json_children = &json["children"]; let json_children = &json["children"];
if context.unit_ctx.data.is_some() {
let unit_ctx = &mut context.unit_ctx;
let bb_head = *unit_ctx.bb_head.last().unwrap();
let mut builder = UnitContext::builder(&mut unit_ctx.data);
builder.append_to(bb_head);
match json_children.len() { match json_children.len() {
1 => { 1 => {
let json_child = &json_children[0]; let json_child = &json_children[0];
match json_child["tag"].as_str() { match json_child["tag"].as_str() {
Some(Tag::DEC_NUMBER) => { Some(Tag::DEC_NUMBER) => {
let w = unit_ctx let value = BigInt::parse_bytes(
.ty_active json_child["text"].as_str().unwrap().as_bytes(),
.as_ref() 10,
.unwrap() )
.unwrap_signal() .unwrap();
.unwrap_int(); let count_bits = |x: &BigInt| -> usize {
let v = json_child["text"].to_string().parse::<usize>().unwrap(); 8 * x.to_biguint().unwrap().to_bytes_be().len()
let num = builder.ins().const_int((w, v)); };
Some(num)
IntValue::from_signed(count_bits(&value), value)
} }
_ => panic!("unknown error at CST node '{}'", json_child), _ => panic!("unknown error at CST node '{}'", json_child),
} }
@ -119,33 +122,45 @@ impl Expression {
2 => { 2 => {
let json_width = &json_children[0]; let json_width = &json_children[0];
assert_eq!(json_width["tag"], Tag::DEC_NUMBER); assert_eq!(json_width["tag"], Tag::DEC_NUMBER);
let w = json_width["text"].to_string().parse::<usize>().unwrap();
assert_eq!(json_children[1]["tag"], Tag::BASE_DIGITS); assert_eq!(json_children[1]["tag"], Tag::BASE_DIGITS);
assert_eq!(json_children[1]["children"].len(), 2); assert_eq!(json_children[1]["children"].len(), 2);
let json_base = &json_children[1]["children"][0]; let json_base = &json_children[1]["children"][0];
let json_digits = &json_children[1]["children"][1]; let json_digits = &json_children[1]["children"][1];
match json_base["tag"].as_str() { let radix = match json_base["tag"].as_str() {
Some(Tag::HEX_BASE) => { Some(Tag::HEX_BASE) => {
assert_eq!(json_digits["tag"], Tag::HEX_DIGITS); assert_eq!(json_digits["tag"], Tag::HEX_DIGITS);
let v = 16
usize::from_str_radix(json_digits["text"].as_str().unwrap(), 16)
.unwrap();
let num = builder.ins().const_int((w, v));
Some(num)
} }
Some(Tag::BIN_BASE) => { Some(Tag::BIN_BASE) => {
assert_eq!(json_digits["tag"], Tag::BIN_DIGITS); assert_eq!(json_digits["tag"], Tag::BIN_DIGITS);
let v = usize::from_str_radix(json_digits["text"].as_str().unwrap(), 2) 2
}
Some(Tag::DEC_BASE) => {
assert_eq!(json_digits["tag"], Tag::DEC_DIGITS);
10
}
_ => panic!("unknown error at CST node '{}'", json),
};
let width = json_width["text"].to_string().parse::<usize>().unwrap();
let value =
BigInt::parse_bytes(json_digits["text"].as_str().unwrap().as_bytes(), radix)
.unwrap(); .unwrap();
let num = builder.ins().const_int((w, v));
Some(num) IntValue::from_signed(width, value)
} }
_ => panic!("unknown error at CST node '{}'", json), _ => panic!("unknown error at CST node '{}'", json),
} }
} }
_ => panic!("unknown error at CST node '{}'", json),
} fn gen_number(json: &JsonValue, context: &mut ModuleContext) -> Option<Value> {
if context.unit_ctx.data.is_some() {
let unit_ctx = &mut context.unit_ctx;
let bb_head = *unit_ctx.bb_head.last().unwrap();
let mut builder = UnitContext::builder(&mut unit_ctx.data);
builder.append_to(bb_head);
let value = builder.ins().const_int(Self::parse_number(json));
Some(value)
} else { } else {
None None
} }
@ -187,6 +202,29 @@ impl Expression {
let value = builder.ins().and(opd_l, opd_r); let value = builder.ins().and(opd_l, opd_r);
Some(value) Some(value)
} }
Some("&&") => {
let nez = |x: Value, builder: &mut UnitBuilder| -> Value {
let ty = &builder.unit().value_type(x);
let zero = builder.ins().const_zero(ty);
builder.ins().neq(x, zero)
};
let nez_l = nez(opd_l, &mut builder);
let nez_r = nez(opd_r, &mut builder);
let value = builder.ins().and(nez_l, nez_r);
Some(value)
}
Some("^") => {
let value = builder.ins().xor(opd_l, opd_r);
Some(value)
}
Some(">=") => {
let value = builder.ins().sge(opd_l, opd_r);
Some(value)
}
Some("<=") => {
let value = builder.ins().sle(opd_l, opd_r);
Some(value)
}
Some(tag) => { Some(tag) => {
error!("FIXME: case '{}' missing", tag); error!("FIXME: case '{}' missing", tag);
None None
@ -216,10 +254,64 @@ impl Expression {
let value = builder.ins().neq(expr.unwrap(), zero); let value = builder.ins().neq(expr.unwrap(), zero);
Some(value) Some(value)
} }
Some("~") => {
let value = builder.ins().not(expr.unwrap());
Some(value)
}
_ => panic!("unknown error at CST node '{}'", json), _ => panic!("unknown error at CST node '{}'", json),
} }
} else { } else {
None None
} }
} }
fn gen_concatenation_expression(
json: &JsonValue,
context: &mut ModuleContext,
) -> Option<Value> {
let json_expressions = Tools::match_tags(
vec![json],
vec![
Tag::CONCATENATION_EXPRESSION,
Tag::OPEN_RANGE_LIST,
Tag::EXPRESSION,
],
);
let expressions: Vec<_> = json_expressions
.iter()
.rev()
.map(|x| Self::codegen(x, context))
.collect();
if context.unit_ctx.data.is_some() {
let unit_ctx = &mut context.unit_ctx;
let bb_head = *unit_ctx.bb_head.last().unwrap();
let mut builder = UnitContext::builder(&mut unit_ctx.data);
builder.append_to(bb_head);
let widths: Vec<_> = expressions
.iter()
.map(|e| {
let v = e.unwrap();
let ty = builder.unit().value_type(v);
assert!(ty.is_int(), "FIXME: type '{}'", ty);
ty.unwrap_int()
})
.collect();
let tot_width = widths.iter().map(|&x| x).reduce(|x, y| x + y).unwrap();
let mut last_slice = builder.ins().const_zero(&int_ty(tot_width));
let mut width = 0;
for (i, e) in expressions.iter().enumerate() {
let v = e.unwrap();
let w = widths[i];
last_slice = builder.ins().ins_slice(last_slice, v, width, w);
width += w;
}
Some(last_slice)
} else {
None
}
}
} }

View File

@ -1,9 +1,9 @@
use crate::cst::{AlwaysStatement, ModuleContext, SymbolInfo, SymbolKind, Tag, Tools}; use crate::cst::{AlwaysStatement, Expression, ModuleContext, SymbolInfo, SymbolKind, Tag, Tools};
use json::JsonValue; use json::JsonValue;
use llhd::ir::{ use llhd::{
ExtUnit, Module, Signature, UnitBuilder, UnitData, UnitId, UnitKind, UnitName, Value, ir::{ExtUnit, Module, Signature, UnitBuilder, UnitData, UnitId, UnitKind, UnitName, Value},
ty::{int_ty, signal_ty},
}; };
use llhd::ty::{int_ty, signal_ty};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
#[allow(unused_imports)] #[allow(unused_imports)]
@ -62,8 +62,9 @@ impl ModuleDeclaration {
raw_name_set.insert(raw_name.to_string()); raw_name_set.insert(raw_name.to_string());
} }
let mut declare = |info_list: &Vec<(String, usize)>, kind: SymbolKind| { let mut declare = |info_list: &Vec<(String, usize, Option<&JsonValue>)>,
for (name, width) in info_list { kind: SymbolKind| {
for (name, width, _) in info_list {
if !context.symbol.contains_key(name) { if !context.symbol.contains_key(name) {
let n = name.to_string(); let n = name.to_string();
let w = width.clone(); let w = width.clone();
@ -75,7 +76,7 @@ impl ModuleDeclaration {
match kind { match kind {
SymbolKind::Reg | SymbolKind::Wire => { SymbolKind::Reg | SymbolKind::Wire => {
for (name, width) in info_list { for (name, width, _) in info_list {
if !raw_name_set.contains(name) { if !raw_name_set.contains(name) {
let n = name.to_string(); let n = name.to_string();
let w = width.clone(); let w = width.clone();
@ -92,6 +93,13 @@ impl ModuleDeclaration {
} }
} }
} }
SymbolKind::Param => {
for (name, _, jn) in info_list {
let n = name.to_string();
let v = Expression::parse_number(jn.unwrap());
context.param.insert(n, v);
}
}
x => warn!("TODO: case '{}'", x), x => warn!("TODO: case '{}'", x),
} }
}; };
@ -340,7 +348,7 @@ impl ModuleDeclaration {
) )
} }
fn get_reg_info(json: &JsonValue) -> Vec<(String, usize)> { fn get_reg_info(json: &JsonValue) -> Vec<(String, usize, Option<&JsonValue>)> {
let base_path = [ let base_path = [
Tag::MODULE_DECLARATION, Tag::MODULE_DECLARATION,
Tag::MODULE_ITEM_LIST, Tag::MODULE_ITEM_LIST,
@ -384,7 +392,7 @@ impl ModuleDeclaration {
Self::get_var_info(json_regs, name_path.to_vec(), dim_path.to_vec()) Self::get_var_info(json_regs, name_path.to_vec(), dim_path.to_vec())
} }
fn get_wire_info(json: &JsonValue) -> Vec<(String, usize)> { fn get_wire_info(json: &JsonValue) -> Vec<(String, usize, Option<&JsonValue>)> {
let base_path = [ let base_path = [
Tag::MODULE_DECLARATION, Tag::MODULE_DECLARATION,
Tag::MODULE_ITEM_LIST, Tag::MODULE_ITEM_LIST,
@ -419,11 +427,11 @@ impl ModuleDeclaration {
Self::get_var_info(json_wires, name_path.to_vec(), dim_path.to_vec()) Self::get_var_info(json_wires, name_path.to_vec(), dim_path.to_vec())
} }
fn get_var_info( fn get_var_info<'a>(
json_vec: Vec<&JsonValue>, json_vec: Vec<&'a JsonValue>,
name_path: Vec<&str>, name_path: Vec<&str>,
dim_path: Vec<&str>, dim_path: Vec<&str>,
) -> Vec<(String, usize)> { ) -> Vec<(String, usize, Option<&'a JsonValue>)> {
json_vec json_vec
.iter() .iter()
.map(|&json| { .map(|&json| {
@ -434,8 +442,8 @@ impl ModuleDeclaration {
json_names json_names
.iter() .iter()
.map(|x| (x["text"].to_string(), width)) .map(|x| (x["text"].to_string(), width, None)) // hack
.collect::<Vec<(String, usize)>>() .collect::<Vec<(String, usize, Option<&JsonValue>)>>()
}) })
.flatten() .flatten()
.collect() .collect()
@ -455,7 +463,7 @@ impl ModuleDeclaration {
} }
} }
fn get_param_info(json: &JsonValue) -> Vec<(String, usize)> { fn get_param_info(json: &JsonValue) -> Vec<(String, usize, Option<&JsonValue>)> {
let base_path = [ let base_path = [
Tag::MODULE_DECLARATION, Tag::MODULE_DECLARATION,
Tag::MODULE_ITEM_LIST, Tag::MODULE_ITEM_LIST,
@ -469,12 +477,11 @@ impl ModuleDeclaration {
Tag::SYMBOL_IDENTIFIER, Tag::SYMBOL_IDENTIFIER,
]; ];
let dim_path = [ let num_path = [
Tag::PARAM_DECLARATION, Tag::PARAM_DECLARATION,
Tag::TRAILING_ASSIGN, Tag::TRAILING_ASSIGN,
Tag::EXPRESSION, Tag::EXPRESSION,
Tag::NUMBER, Tag::NUMBER,
Tag::DEC_NUMBER,
]; ];
let json_params: Vec<&JsonValue> = Tools::match_tags(vec![json], base_path.to_vec()) let json_params: Vec<&JsonValue> = Tools::match_tags(vec![json], base_path.to_vec())
@ -489,12 +496,15 @@ impl ModuleDeclaration {
let json_names = Tools::match_tags(vec![json], name_path.to_vec()); let json_names = Tools::match_tags(vec![json], name_path.to_vec());
assert_eq!(json_names.len(), 1); assert_eq!(json_names.len(), 1);
let json_dims = Tools::match_tags(vec![json], dim_path.to_vec()); let json_num = Tools::match_tags(vec![json], num_path.to_vec());
assert_eq!(json_dims.len(), 1); assert_eq!(json_num.len(), 1);
let v = Expression::parse_number(json_num[0]);
let width = json_dims[0]["text"].to_string().parse::<usize>().unwrap(); (
json_names[0]["text"].to_string(),
(json_names[0]["text"].to_string(), width) v.width,
Some(json_num[0]),
)
}) })
.collect() .collect()
} }

View File

@ -1,5 +1,6 @@
use crate::cst::{Expression, ModuleContext, Tag, Tools}; use crate::cst::{Expression, ModuleContext, Tag, Tools, UnitContext};
use json::JsonValue; use json::JsonValue;
use llhd::{ir::Block, value::TimeValue};
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
@ -7,20 +8,17 @@ use log::{debug, error, info, trace, warn};
pub struct NetVariableAssignment {} pub struct NetVariableAssignment {}
impl NetVariableAssignment { impl NetVariableAssignment {
pub fn codegen(json: &JsonValue, context: &mut ModuleContext) { pub fn codegen(json: &JsonValue, context: &mut ModuleContext) -> Option<Block> {
let (json_lpvalue, json_expression) = {
assert_eq!(json["tag"], Tag::NET_VARIABLE_ASSIGNMENT); assert_eq!(json["tag"], Tag::NET_VARIABLE_ASSIGNMENT);
let (json_lpvalue, json_expression) = {
let json_children = &json["children"]; let json_children = &json["children"];
assert_eq!(json_children.len(), 4); assert_eq!(json_children.len(), 4);
assert_eq!(json_children[1]["tag"], "="); assert_eq!(json_children[1]["tag"], "=");
assert_eq!(json_children[3]["tag"], ";"); assert_eq!(json_children[3]["tag"], ";");
(&json_children[0], &json_children[2])
};
let json_symbol_identifier = Tools::match_tags( let json_symbol_identifier = Tools::match_tags(
vec![json_lpvalue], vec![&json_children[0]],
vec![ vec![
Tag::LP_VALUE, Tag::LP_VALUE,
Tag::REFERENCE_CALL_BASE, Tag::REFERENCE_CALL_BASE,
@ -31,13 +29,56 @@ impl NetVariableAssignment {
], ],
); );
assert_eq!(json_symbol_identifier.len(), 1); assert_eq!(json_symbol_identifier.len(), 1);
context
.unit_ctx
.lvalues
.insert(json_symbol_identifier[0]["text"].to_string());
Expression::codegen(json_expression, context); (json_symbol_identifier[0], &json_children[2])
};
warn!("TODO: finish codegen()"); let lvalue_name = &json_lpvalue["text"].to_string();
context.unit_ctx.lvalues.insert(lvalue_name.clone());
if context.unit_ctx.raw_name_to_value.contains_key(lvalue_name) {
assert!(context.unit_ctx.raw_name_to_arg.contains_key(lvalue_name));
assert!(context.unit_ctx.data.is_some());
let arg = context.unit_ctx.raw_name_to_arg[lvalue_name];
let builder = &UnitContext::builder(&mut context.unit_ctx.data);
// set active Type
context.unit_ctx.ty_active = Some(builder.unit().sig().arg_type(arg));
}
let opt_expr = Expression::codegen(json_expression, context);
if context.unit_ctx.raw_name_to_value.contains_key(lvalue_name) {
// unset active Type
context.unit_ctx.ty_active = None;
}
let unit_ctx = &mut context.unit_ctx;
if unit_ctx.data.is_some() {
if opt_expr.is_none() {
error!("FIXME: CST node '{}'", json_expression.to_string());
panic!();
}
let bb_head = *unit_ctx.bb_head.last().unwrap();
let mut builder = UnitContext::builder(&mut unit_ctx.data);
builder.append_to(bb_head);
let delta_time = builder.ins().const_time(TimeValue::new(num::zero(), 1, 0));
builder.ins().drv(
unit_ctx.raw_name_to_value[lvalue_name],
opt_expr.unwrap(),
delta_time,
);
if unit_ctx.raw_name_to_shadow.contains_key(lvalue_name) {
warn!("TODO: double check");
builder.ins().st(
unit_ctx.raw_name_to_shadow[lvalue_name],
unit_ctx.raw_name_to_value[lvalue_name],
);
}
Some(bb_head)
} else {
None
}
} }
} }

View File

@ -19,10 +19,7 @@ impl Statement {
Some(Tag::NONBLOCKING_ASSIGNMENT_STATEMENT) => { Some(Tag::NONBLOCKING_ASSIGNMENT_STATEMENT) => {
NonblockingAssignmentStatement::codegen(json, context) NonblockingAssignmentStatement::codegen(json, context)
} }
Some(Tag::NET_VARIABLE_ASSIGNMENT) => { Some(Tag::NET_VARIABLE_ASSIGNMENT) => NetVariableAssignment::codegen(json, context),
NetVariableAssignment::codegen(json, context);
None
}
_ => panic!("unknown error at CST node '{}'", json), _ => panic!("unknown error at CST node '{}'", json),
} }
} }

View File

@ -45,11 +45,13 @@ impl Tag {
pub const REFERENCE: &'static str = "kReference"; pub const REFERENCE: &'static str = "kReference";
pub const LOCAL_ROOT: &'static str = "kLocalRoot"; pub const LOCAL_ROOT: &'static str = "kLocalRoot";
pub const EXPRESSION_LIST: &'static str = "kExpressionList";
pub const EXPRESSION: &'static str = "kExpression"; pub const EXPRESSION: &'static str = "kExpression";
pub const BINARY_EXPRESSION: &'static str = "kBinaryExpression"; pub const BINARY_EXPRESSION: &'static str = "kBinaryExpression";
pub const UNARY_PREFIX_EXPRESSION: &'static str = "kUnaryPrefixExpression"; pub const UNARY_PREFIX_EXPRESSION: &'static str = "kUnaryPrefixExpression";
pub const CONDITION_EXPRESSION: &'static str = "kConditionExpression"; pub const CONDITION_EXPRESSION: &'static str = "kConditionExpression";
pub const CONCATENATION_EXPRESSION: &'static str = "kConcatenationExpression"; pub const CONCATENATION_EXPRESSION: &'static str = "kConcatenationExpression";
pub const OPEN_RANGE_LIST: &'static str = "kOpenRangeList";
pub const NUMBER: &'static str = "kNumber"; pub const NUMBER: &'static str = "kNumber";
pub const BASE_DIGITS: &'static str = "kBaseDigits"; pub const BASE_DIGITS: &'static str = "kBaseDigits";
@ -58,6 +60,8 @@ impl Tag {
pub const HEX_DIGITS: &'static str = "TK_HexDigits"; pub const HEX_DIGITS: &'static str = "TK_HexDigits";
pub const BIN_BASE: &'static str = "TK_BinBase"; pub const BIN_BASE: &'static str = "TK_BinBase";
pub const BIN_DIGITS: &'static str = "TK_BinDigits"; pub const BIN_DIGITS: &'static str = "TK_BinDigits";
pub const DEC_BASE: &'static str = "TK_DecBase";
pub const DEC_DIGITS: &'static str = "TK_DecDigits";
pub const ALWAYS_STATEMENT: &'static str = "kAlwaysStatement"; pub const ALWAYS_STATEMENT: &'static str = "kAlwaysStatement";
pub const ALWAYS: &'static str = "always"; pub const ALWAYS: &'static str = "always";